JavaScript - this - details

52 阅读3分钟

friends use this questions on work.

这里为全局作用域 GO (Global Object) 
console.log(this); // 毫无疑问是window(因为全局作用域下this指向window)

function foo() {
    console.log(this);
};
foo(); // window

问题来了明明function是有作用域的为什么this 还是 window呢?
答:因为this指向与使用地无关

先记住几个概念:
    one: 单独函数调用this指向就是window(默认绑定)
    two: 谁引用this就指向谁(隐式绑定)
    three: 通过 call、apply、bind(显示绑定)
    four: 通过 new 关键字绑定(new绑定)
    
const obj = {
    // 为什么不直接用 name 而用 oname ? 因为养成习惯避免直接使用name (window中有个字段是name)
    oname: 'obj',
    fn: foo
};
obj.fn(); 注意此时 this 就隐式绑定到 obj 上了(所以foo函数内输入this此时是obj)

obj.fn.call(); // 通过 call - apply 调用函数(这里虽然使用了call and apply 但是并没有显示的绑定this)

obj.fn.call(1); // 注意此时 this 为 1 因为你显示绑定了(除了 null and undefined不可以绑定为 this 其它的都可以)
obj.fn.apply(1); // 也是同理

// 那么 bind 是怎么用的呢?
const newFn = obj.fn.bind(1);
newFn(); // 此时 this 为 1

// 那么它们3个的区别又是什么呢?
    共同点:都可以绑定 this 并传递参数
    雷同点:都可以不传参数不绑定 this 直接调用函数
    不同点:
            call: 传递参数时以逗号分隔(与正常调用函数传递实参一致)
            apply: 传递参数时必须是一个数组数组内对应的每个元素就是一个参数
            bind: 传递参数时也是使用逗号分隔和call类似但是不同的是 bind 可以在两个地方传递参数
                例:const newFn = xxx.bind(this, ...params);
                    newFn(...params)
                    其实内部就是形成了一个天然的闭包(不懂什么是闭包? 可以看一下我上一篇文章)
            
但需要注意的是箭头函数是没有作用域的所以如果在箭头函数内使用 this 会一层一层的找出去找到谁 谁就是this
所以这也就是说为什么上篇文章我说能访问外部变量的函数就是闭包

// 有兴趣的朋友可以看下这是为什么
const obj = {
    name: 'susu',
    eating() {

    },
    running() {
        var name = '鸭鸭'
    },
    studying() {
        console.log(this);
        console.log(`${this.name}在学习`);
    }
};
obj.studying.apply(obj.running);

下篇文章讲 call - apply - bind 的实现原理

深度解析

function foo() {
    console.log(this, '被调用了');
}

var obj = {
    Symbol: 'obj'
}

foo();

foo.call();
foo.apply();

foo.call(obj);
foo.apply(obj);

foo.call('bbb');
foo.apply('bbb');

var newFoo = foo.bind('aaa');
newFoo();

function bar(number1, number2) {
    console.log(number1 + number2, this);
}

var newBar = bar.bind();
newBar();

bar.call('call', 20, 20);
bar.apply('apply', [20, 20]);
// call、apply、bind详解
// 共同点:都是绑定this都可以在绑定this时传递参数
/* 
    1.call与bind类似(在绑定this的同时可传递参数每个参数以逗号分隔) 
    2.apply传递参数时用数组数组内每个参数用逗号分隔
    3.call与apply都可以不绑定this不传递参数直接调用函数
    4.bind虽然也可以但是bind的使用必须要有变量接收地址的引用
    优先级:默认绑定 < 隐式绑定 < 显示绑定 < new绑定
    忽略显示绑定:当bind、call、apply绑定 null、undefined 会绑定不上(不改变原this)
    箭头函数不绑定this(无论你使用什么方式都不会绑定上this并且箭头函数不能使用new否则会报错)箭头函数找不到this会往上层找如果一直找不到就是最外层的window
    严格模式(use strict) 默认绑定指向undefined
*/


function Person(name, age) {
    this.name = name;
    this.age = age;
}

var p1 = new Person('susu', 18);
console.log(p1);

var p2 = Person('susu', 18);
console.log(p2);