完全弄懂JavaScript中的this

来源:互联网 发布:怎样在淘宝网卖闲置品 编辑:程序博客网 时间:2024/06/07 00:37

原文网址:http://www.smartcitychina.cn/QianYanJiShu/2016-07/7535.html?ref=myread

其实this是一个老生常谈的问题了。关于this的文章非常多,其实我本以为自己早弄明白了它,不过昨天在做项目的过程中,还是出现了一丝疑惑,想到大概之前在JavaScript weekly里收藏待看的一篇详解this的文章(后有链接,也附上了稀土上的中文译文)和另一篇一位前辈推荐的文章,就把它们看了看,对this的认识确实提升了一些。

JavaScript 中的’this‘是动态的,它在函数运行时被确定而非在函数声明时被确定。所有的函数都可以调用'this',这无关于该函数是否属于某个对象。关于this,主要有以下四种情况。

1.被当做对象的方法被调用

如果该函数是被当做某一个对象的方法,那么该函数的this指向该对象;

var john = {      firstName: "John"    }    function func() {      alert(this.firstName + ": hi!")    }    john.sayHi = func    john.sayHi()  // this = john

这里有一点值得注意,当一个对象的方法被取出来赋值给一个变量时,该方法变为函数触发,this指向window或underfind(严格模式)。

2.函数之内调用

当函数中有 this ,其实就意味着它被当做方法调用,之间调用相当于把他当做window对象的方法,this指向window,值得注意的是ES5其实是规定这种情况 this=undefined 的,只浏览器大多还是按照老的方法执行(本人在最新版的Chrome,Safari,Firefox中测试都指向window(201607)),在火狐下使用严格模式指向undefined;

func()    function func() {        alert(this) // [object Window] or [object global] or kind of..    }

为了传递 this ,()之前应该为引用类型,类似于 obj.a 或者 obj['a'] ,不能是别的了。

这里还存在一个小坑,当对象的方法中还存在函数时,该函数其实是当做函数模式触发,所以其 this 默认为window(严格模式下为undefined)解决办法是给该函数绑定this。

var numbers = {     numberA: 5,   numberB: 10,   sum: function() {     console.log(this === numbers); // => true     function calculate() {       // this is window or undefined in strict mode       console.log(this === numbers); // => false       return this.numberA + this.numberB;     }     return calculate();   }};numbers.sum(); // => NaN or throws TypeError in strict mode

var numbers = {     numberA: 5,   numberB: 10,   sum: function() {     console.log(this === numbers); // => true     function calculate() {       console.log(this === numbers); // => true       return this.numberA + this.numberB;     }     // use .call() method to modify the context     return calculate.call(this);   }};numbers.sum(); // => 15

3.在 new 中调用

一个引用对象的变量实际上保存了对该对象的引用,也就是说变量实际保存的是对真实数据的一个指针。

使用new关键字时 this 的改变其实有以下几步:

  • 创建 this = {} .

  • new执行的过程中可能改变 this ,然后添加属性和方法;

  • 返回被改变的 this .

function Animal(name) {        this.name = name        this.canWalk = true    }    var animal = new Animal("beastie")    alert(animal.name)
需要注意的是如果构造函数返回一个对象,那么this指向返回的那个对象;

function Animal() {        this.name = 'Mousie';        this.age = '18';        return {            name: 'Godzilla'        } // <-- will be returned    }    var animal = new Animal()    console.log(animal.name) // Godzilla    console.log(animal.age)//undefined
这里需要注意的是不要忘记使用new,否则不会创建一个新的函数。而是只是执行了函数,相当于函数调用,this其实指向window

function Vehicle(type, wheelsCount) {    this.type = type;  this.wheelsCount = wheelsCount;  return this;}// Function invocationvar car = Vehicle('Car', 4);  car.type;       // => 'Car'  car.wheelsCount // => 4  car === window  // => true

4.明确调用 this ,使用 call 和 apply

这是最具JavaScript特色的地方。

如下代码:

func.call(obj, arg1, arg2,...)
第一个参数将作为 this 的指代对象,之后的参数将被作为函数的参数,解决方法是使用bind。

function Animal(type, legs) {    this.type = type;  this.legs = legs;    this.logInfo = function() {    console.log(this === myCat); // => true    console.log('The ' + this.type + ' has ' + this.legs + ' legs');  };}var myCat = new Animal('Cat', 4);  // logs "The Cat has 4 legs"setTimeout(myCat.logInfo.bind(myCat), 1000); // setTimeout??
var john = {    firstName: "John",    surname: "Smith"  }  function func(a, b) {    alert( this[a] + ' ' + this[b] )  }  func.call(john, 'firstName', 'surname')  // "John Smith"
至于apply,其只是以数组的方传入参数,其它部分是一样的,如下:

func.call(john, 'firstName', 'surname')  func.apply(john, ['firstName', 'surname'])
它们也可用于在 ES5 中的类继承中,调用父级构造器。

function Runner(name) {        console.log(this instanceof Rabbit); // => true      this.name = name;      }    function Rabbit(name, countLegs) {        console.log(this instanceof Rabbit); // => true      // 间接调用,调用了父级构造器      Runner.call(this, name);      this.countLegs = countLegs;    }    var myRabbit = new Rabbit('White Rabbit', 4);      myRabbit; // { name: 'White Rabbit', countLegs: 4 }

5. .bind()

对比方法 .apply() 和 .call(),它俩都立即执行了函数,而 .bind() 函数返回了一个新方法,绑定了预先指定好的 this ,并可以延后调用。

.bind() 方法的作用是创建一个新的函数,执行时的上下文环境为 .bind() 传递的第一个参数,它允许创建预先设置好 this 的函数。

var numbers = {    array: [3, 5, 10],  getNumbers: function() {    return this.array;      }};// Create a bound functionvar boundGetNumbers = numbers.getNumbers.bind(numbers);  boundGetNumbers(); // => [3, 5, 10]  // Extract method from objectvar simpleGetNumbers = numbers.getNumbers;  simpleGetNumbers(); // => undefined or throws an error in strict mode

使用 .bind() 时应该注意,.bind() 创建了一个永恒的上下文链并不可修改。一个绑定函数即使使用 .call() 或者 .apply()传入其他不同的上下文环境,也不会更改它之前连接的上下文环境,重新绑定也不会起任何作用。

只有在构造器调用时,绑定函数可以改变上下文,然而这并不是特别推荐的做法。

6.箭头函数

箭头函数并不创建它自身执行的上下文,使得 this 取决于它在定义时的外部函数。

箭头函数一次绑定上下文后便不可更改,即使使用了上下文更改的方法:

var numbers = [1, 2];      (function() {        var get = () => {        console.log(this === numbers); // => true        return this;      };      console.log(this === numbers); // => true      get(); // => [1, 2]      // 箭头函数使用 .apply() 和 .call()      get.call([0]);  // => [1, 2]      get.apply([0]); // => [1, 2]      // Bind      get.bind([0])(); // => [1, 2]    }).call(numbers);
这是因为箭头函数拥有静态的上下文环境,不会因为不同的调用而改变。因此不要使用箭头函数定义方法

function Period (hours, minutes) {        this.hours = hours;      this.minutes = minutes;    }    Period.prototype.format = () => {        console.log(this === window); // => true      return this.hours + ' hours and ' + this.minutes + ' minutes';    };    var walkPeriod = new Period(2, 30);      walkPeriod.format(); // => 'undefined hours and undefined minutes'














0 0
原创粉丝点击