作用域

一、示例

    console.log(str);        // 输出undefined  因为读取的是解析器预先存储的str,还没有被赋值
    var str = 1;         


二、示例2

    alert(a);        // 弹出的是function a(){ alert("2")}
    var a = 1;
    function a(){
        alert("2")
    }

    预解析会保存a = undefinded,和 a = function a(){alert("2")},
    在变量与函数名相同时,输出的结果函数优先级高于变量名


三、示例3

    var a = 1;
    function a(){
        alert("2");
    }

    // 报错,因为a被定义成变量,typeof a 返回的是number,如果 var a;不能定义值那么a()是成功的
    a();


四、示例4

    var a = 1;
    function fn1(){
        alert(a);
        // 注意这里因为是使用var声明所以执行数预解析器才会把它单独存储起来,
        // 如果将var去掉,a=2改成了赋值这样会改变外部的a的值,下面输出的就是1,2
        var a = 2;
    }

    fn1();                        // undefined
    console.log(a);        // 1


五、示例5

    function foo(){
        return fn1;

        function fn1(){};
        var fn1 = 10;
    }

    var f = foo();

    // 输出 function fn1(){};    因为调用foo函数时,return  fn1因为变量还未定义所以 直接调用的函数
    alert(f);                


六、示例6

    var  myname = "global"; // 全局变量
    function fun() {
        alert(myname);             // "undefined",如果函数内不在定义myname就会找全局的变量,但函数本身已经定义了,预编译会先将变量定义成undefined        
        var myname = "local";
        alert(myname);             // "local"
    }
    fun();


七、示例7

    <script>
        window.name = 'globalName';

        var getName = function(){
            return this.name;
        }

        getName();         // 'globalName'
    </script>


八、示例8

    <script>
        window.id = 'window';

        document.getElementById('divA').onclick = function(){
            alert(this.id);            // output "divA"

            var callback = function(){
                return this.id;        // output "window"
            }
            callback();            // 指针指向window
        }
    </script>


 九、示例9

    1、var getName;
        console.log(getName)    // undefined

        getName()                // Uncaught TypeError: getName is not a function

        var getName = function() {
            console.log('wscat')
        }

    2、var getName;
        console.log(getName)    // function getName() {console.log('oaoafly')}

        getName()                // oaoafly

        function getName() {
            console.log('oaoafly')
        }


十、bind()

    <script>
        window.id = 'window';

        document.getElementById('divA').onclick = function(){
            alert(this.id);

            var obj = {
                id: 'objId',
                callback: function(){
                    return this.id;         // 'divA'
                }.bind(window)                // 通过bind来将callback内的this指向window
            }

            console.log(obj.callback());    // 'window'
        }
    </script>


十一、严格模式下的this

    <script>
        function fun(){
            'use strict'
            alert(this);        // 返回 'undefined', strict模式下this不会指向全局
        }

        fun();
    </script>


十二、箭头函数下的this指向

    let Template = {
        test(){
            console.log(this);        // this指向Template

            document.querySelector("#showThis").onclick = () =>{
                // 如果非箭头函数this应该指向 #showThis
                // 箭头函数不是指向调用者,所以指向了Template
                console.log(this);    
            }
        }
    }
    Template.test();

构造函数

1、函数本身是对象也是构造函数

2、直接挂载到函数下,是对象的属性和方法

    function Foo(){};
    Foo.username = '111';     // 注意这里Foo的属性不能为name因为被name被函数的名所占用
    Foo.getName = function(){ console.log(this.username) };

    // 调用
    Foo.getName();        // 111

3、在构造函数的内部属性和方法

    function Foo() {
        this.username = '111'
    }
    Foo.username = '222';

    // 调用
    console.log(new Foo().username);    // 111

<script>
    function Foo() {
        getName = function() {
            console.log(this);
            console.log(1);
        }

        console.log(this);
        return this;
    }
    Foo.getName = function() { // 这里只是Foo对象的方法,而不是构造函数的方法,
        console.log(2);
    }

    // Foo.prototype.getName = function() {
    //     console.log(3);
    // }

    var getName = function() {
        console.log(4)
    }

    function getName() {
        console.log(5)
    }

    Foo.getName(); // 2    Foo.getName=function(){}  是存到Foo下的方法,Foo.getName()可以取到
    getName(); // 4     如果一个作用域下有var的变量名与函数名相当,var的作用域提升高于function
    Foo().getName();     // 1  因为Foo()被调用,定义中加了return this,Foo指向的是window全局,getName没有var升明,所以为全局,所以相当于window.getName(),Foo中如果不加return this那么就会报错,因为Foo没有链式调用,所以this断了
    getName(); // 1   因为上面已经将Foo内的getName()提升成全局,所以会赋盖掉var getName、function getName定义的全局方法

    // var ofo = new Foo;
    // ofo.getName();   // 3

    // Foo既是一个对象也是一个构造函数
    new Foo.getName();             // 2  先执行Foo.getName(),因为"."运算符优先于new运算符,所以这时候Foo只是一个对象并不是构造函数的实例,所以会调用对象下的方法
    new Foo().getName();         // 1  先执行new Foo()构造函数,然后在构造函数下去找getName方法,Foo构造函数方法没有getName()所以到prototype下去找
    new new Foo().getName();     // 1  与上面相同
</script>

对象拷贝并不引用指针

有两种方法,一是使用for...in将一个对象拷贝到另一个对象

另一种方法是使用Object.assign()

let objA = {
        name: 'siguang',
        age: 30
}
let objB = Object.assign({}, objA);
objB.name = 'lulu';         // lulu
console.log(objA.name);     // siguang

对象的深拷贝和浅拷贝

浅拷贝就是对象之间还存引用关系,深拷贝就是两个对象之间没有引用关系

一、浅拷贝

    const a = {t: 1, p: 'gg'};
    const b = a;
    b.t = 3;
    console.log(a);     // {t: 3, p: 'gg'}
    console.log(b);     // {t: 3, p: 'gg'}

二 、深拷贝

    1、Object.assign() 方法是将两个对象合并,并返回一个新对象

        **** 注意assig()只能拷贝一层,多层结构还是浅拷贝 ****

        const c = {t: 1, p: 'gg'};
        const d = Object.assign({}, c);
        d.t = 3;
        console.log(c);     // {t: 1, p: 'gg'}
        console.log(d);     // {t: 3, p: 'gg'}

    2、Object.create() 将拷贝的内容入到prototype下,对象和数组都适用

        对象:

            var oA = {
                name: 'sg',
                age: 33
            }
            var oB = {};
            oB = Object.create(oA);

            oB.eat = 'bread';

        数组:

            var oA = [1,2]
            var oB = [];
            oB = Object.create(oA);

            oB.push(3);            // oB.length;   3        

    3、jQuery.extend()

        let oa = {a: 1, b: 2}; let ob = {};  
        $.extend(ob, oa); 
        ob.b = 3; 
        console.log(oa, ob);        // oa: {a: 1, b: 2}、 ob: {a: 1, b: 3}

数组的深拷贝

一、concat()方法

    var arrA = [1,2,3];
    var arrB = arrA.concat();
    arrB.push(5,6,7);
    console.log(arrA);  // [1,2,3]
    console.log(arrB);  // [1,2,3,5,6,7]

二、slice()方法

    var arrA = [1,2,3];
    var arrB = arrA.slice();
    arrB.push(5,6,7);
    console.log(arrA);  // [1,2,3]
    console.log(arrB);  // [1,2,3,5,6,7]

两种方法都是回返一个不带指向的新数组

三、使用扩展运算符...

    let a1 = [1, 2, 3];
    let a2 = [...a1];
    a2.push(4);

    console.log(a1);        // [1, 2, 3]
    console.log(a2);        // [1, 2, 3, 4]

对象处理

一、查看是否是一个空对象

    // 使用keys来查看对象中所有的kyes并返回一个数组,如果数组为度为0则对象为空
    var obj = {}; 
    var Len = Object.keys(obj);         
    console.log(Len.length);

二、查看对象中属性是否存在

    var obj = {
        a: 1,
        b: 2
    }
    console.log(obj.c);        // undefined

判断数据类型

function isType(data, name){ 
    return Object.prototype.toString.call(data) === `[object ${name}]` 
}
isType({}, 'Object');            // true;

数组处理

一、数据去重

    1、普通方法

        function clearRepeat(arr){
            var newArr = [], obj = {};
            for(var i=0; i<arr.length; i++){
                if(!obj[arr[i]]){
                    obj[arr[i]] = '';        // 对象属性相同会直接覆盖
                    newArr.push(key);        
                }
            }
            return newArr;
        }

        let retArray = clearRepeat([3,2,3,4,5,5,2,2,13,3]);


    2、使用ES6的set()方法,set方法就是不允许数组内有重复的值

        let setValue = new Set([1,2,33,1,22,2,2,4,2,1,2])
        console.log(setValue, Object.prototype.toString.call(setValue));        // {size: 5, [1, 2, 33, 22, 4]}, '[Object Set]'
        let arr = [...setValue];        // [1, 2, 33, 22, 4] 需要进行解构

    http://www.jb51.net/article/46154.htm


二、数组排序

    1、使用sort方法

        var arr = [6,7,5,8,4,2342342,23,234,2,34];
        arr.sort(function(v1, v2){
            return v1 - v2        // 正序
        })
        console.log(arr);


三、返回数组中最大最小值

    let arr = [2,1,423,343,5,67,8];
    let min = Math.min.apply(null, arr);
    let max = Math.max.apply(null, arr);

编写组件并兼容AMD、ES6写法

;(function(root, factory){

    // 判断使用的支持定义模块的方式
    if(typeof define === 'function' && define.amd){
        define(factory)
    }
    else if(typeof exports === 'object'){
        module.exports = factory();
    }
    else{
        root.NProgress = factory();
    }

})(this, function(){
    console.log(`输出内容`);
})

千分位函数

Vue.filter('thousand', function (num, fixed) {
    let n = Number(num)
    if (isNaN(n)) return 0;
    if (fixed && String(n).split('.').length === 2 && String(n).split('.')[1].length > fixed) {
        n = num.toFixed(fixed)
    }
    const str = (n || 0).toString()
    return str.indexOf('.') > -1 ? str.replace(/(\d)(?=(?:\d{3})+\.)/g, '$1,') : str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
});

小技巧

1、获取arguments参数中的不同

    function execute(name){
        return [].slice.call(arguments, 1);
    }
    execute(1,2,3,4);        // 返回 [2, 3, 4];

2、case语句

    function showTest(type) { 
        switch(type) { 
            case "a":                    // type为a/b/c都执行console.log('ok')
            case "b":
            case "c":
                console.log('ok');
                break;

            default:
                console.log('error');
                break;
        }
    }

兼容不支持的方法

1、 Object.create不支持的处理

    Object.create = Object.create || function(obj){
        var F = function(){};
        F.prototype = obj;
        return new F();
    }

2、bind()

    Function.prototype.bind = Function.prototype.bind || function(){
        var self = this,
                context = [].shift.call(arguments),
                args = [].slice.call(arguments);

        return function(){
            return self.apply(context, [].concat.call(args, [].slice.call(arguments)))
        }
    }

| https://juejin.im/entry/58db95eaac502e0058f8472e
| http://www.iamaddy.net/2015/04/front-end-engineering/ // 前端的发展