前端基础知识:this 指向问题,和 bind、call、apply之区分

this 就是一个指针,指向我们调用函数的对象。

执行上下文: 是语言规范中的一个概念,用通俗的话讲,大致等同于函数的执行“环境”。具体的有:变量作用域(和 作用域链条,闭包里面来自外部作用域的变量),函数参数,以及 this 对象的值。

找出 this 的指向

this 的值并不是由函数定义放在哪个对象里面决定,而是函数执行时由谁来唤起决定。

var name = "Jay Global";
var person = {
    name: 'Jay Person',
    details: {
        name: 'Jay Details',
        print: function() {
            return this.name;
        }
    },
    print: function() {
        return this.name;
    }
};

console.log(person.details.print());  // 【details对象调用的print】Jay Details
console.log(person.print());          // 【person对象调用的print】Jay Person

var name1 = person.print;
var name2 = person.details;

console.log(name1()); // 【name1前面没有调用对象,所以是window】Jay Global
console.log(name2.print()) // 【name2对象调用的print】Jay Details

this和箭头函数

箭头函数按词法作用域来绑定它的上下文,所以 this 实际上会引用到原来的上下文。

箭头函数保持它当前执行上下文的词法作用域不变,而普通函数则不会。换句话说,箭头函数从包含它的词法作用域中继承到了 this 的值。

匿名函数,它不会作为某个对象的方法被调用, 因此,this 关键词指向了全局 window 对象。

var object = {
    data: [1,2,3],
    dataDouble: [1,2,3],
    double: function() {
        console.log(this); // object
        return this.data.map(function(item) { // this是当前object,object调用的double
            console.log(this);   // 传给map()的那个匿名函数没有被任一对象调用,所以是window
            return item * 2;
        });
    },
    doubleArrow: function() {
        console.log(this); // object
        return this.dataDouble.map(item => { // this是当前object,object调用的doubleArrow
            console.log(this);      // doubleArrow是object调用的,这就是上下文,所以是window
            return item * 2;
        });
    }
};
object.double();
object.doubleArrow();

明确设置执行上下文

在 JavaScript 中通过使用内置的特性开发者就可以直接操作执行上下文了。这些特性包括:

  • bind():不需要执行函数就可以将 this 的值准确设置到你选择的一个对象上。通过逗号隔开传递多个参数。 设置好 this 关键词后不会立刻执行函数。
  • apply():将 this 的值准确设置到你选择的一个对象上。apply(thisObj, argArray)接收两个参数,thisObj是函数运行的作用域(this),argArray是参数数组,数组的每一项是你希望传递给函数的参数。如果没有提供argArray和thisObj任何一个参数,那么Global对象将用作thisObj。最后,会立刻执行函数。
  • call():将 this 的值准确设置到你选择的一个对象上。然后像bind 一样通过逗号分隔传递多个参数给函数。语法:call(thisObj,arg1,arg2,..., argn);,如果没有提供thisObj参数,那么Global对象被用于thisObj。最后,会立刻执行函数。

this 和 bind

var bobObj = {
    name: "Bob"
};
function print() {
    return this.name;
}
var printNameBob = print.bind(bobObj);
console.log(printNameBob()); // Bob

this 和 call

function add(a, b) { 
    return a + b; 
}
function sum() {
    return Array.prototype.reduce.call(arguments, add);
}
console.log(sum(1,2,3,4)); // 10

this 和 apply

apply 就是接受数组版本的call。

Math.min(1,2,3,4); // 返回 1
Math.min([1,2,3,4]); // 返回 NaN。只接受数字
Math.min.apply(null, [1,2,3,4]); // 返回 1

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

function Student(name, age, grade) {  
  Person.apply(this, arguments);  //Person.call(this, name, age);
  this.grade = grade;  
}
var student = new Student("sansan", 21, "一年级");  
 
console.log("student:", student); // {name: 'sansan'; age: '21', grade: '一年级'}

如果你的参数本来就存在一个数组中,那自然就用 apply,如果参数比较散乱相互之间没什么关联,就用 call。

THE END