this学习(一)
情况一: 在方法中使用this
1 var fruits = { 2 one: "coconut", 3 two: "chestnut ", 4 eat: function () { 5 console.log(this.one + " " + this.two); 6 console.log(fruits.one + " " + fruits.two); 7 } 8 } 9 fruits.eat(); 10 // coconut chestnut 11 // coconut chestnut
在上面的代码中,我们定义了一个具有属性one,two,和eat的对象fruits,其中eat是一个函数,函数体内使用2种不同的方法输出make和model。
使用this时,this.one + " " + this.two 中的 this 指的是在上下文中的对象,也就是fruits,则this.one 为 fruits.one, this.two 为 fruits.two
使用点操作符时,我们可以直接访问对象的属性 fruits.one 和 fruits.two
情况二: 在方法中使用this
函数中的this就有些复杂了。与对象一样,函数也具有属性,函数每次执行时都会获取this,它指向调用它的对象。
如果函数未被某对象调用,则函数内的this属于全局对象,该全局对象被称为 window。在这种情况下,this 将指向全局作用域中的变量。如下例所示:
1 var one = "coconut"; 2 var two = "chestnut" 3 function eat() { 4 console.log(this.one + " " + this.two); 5 } 6 var fruits = { 7 one: "coconut", 8 two: "chestnut", 9 eat: function () { 10 console.log(this.one + " " + this.two); 11 } 12 } 13 fruits.eat(); // coconut chestnut 14 window.eat(); // coconut chestnut 15 eat(); // coconut chestnut
在该例中,在全局对象中定义了one,two,和eat,对象fruits中也实现了eat 方法。当使用fruits调用该方法时,this指向该对象内的变量,而使用另外两种调试方式时,this指向全局变量
情况三: 单独使用this
1 var one = "coconut"; 2 var two = "chestnut" 3 4 console.log(this.one) //coconut 5 console.log(this.two) //chestnut
当单独使用this,不依附与任何函数或者对象时,指向全局对象,这里的this指向全局变量name
情况四: 在事件内使用this
JS中有很多事件类型,但为了描述简单,这里我们以点击事件为例。
每当单机按钮并触发一个事件时,可以调用另一个函数来去执行某个任务。如果在函数内使用this,则指向触发事件中的元素。DOM中,所有的元素都以对象的形式储存,也就是说网页元素实际就是DOM中的一个对象,因此每触发一个事件时,this就会指向该元素。
五: call(), apply() & bind() 的使用
bind: 允许我们在方法中设置this指向
call&apply: 允许我们借助其他函数并在函数调用中改变this的指向
例一
var fruits = { one: "coconut", two: "chestnut", basket: null, eat: function () { this.basket = this.one + " " + this.two; console.log(this.basket); } } var anotherFruits = { one: "apple", two: "pear", basket: null } anotherFruits.basket = fruits.eat();
结果并不是我们所期望的,分析其原因:每当我们使用this调用另一个对象的方法时,只是为了anotherfruits 分配了该方法,但实际调用者是fruits,因此返回的是coconut而不是apple。
我们可以使用call()解决这个问题
var fruits = { one: "coconut", two: "chestnut", basket: null, eat: function () { this.basket = this.one + " " + this.two; console.log(this.basket); } } var anotherFruits = { one: "apple", two: "pear", basket: null } fruits.eat.call(anotherFruits) //apple pear console.log(fruits.basket) //null console.log(anotherFruits.basket) //apple pear
该例中利用call()方法使 anotherFruits 对象调用eat(),该对象中原本并没有eat()方法,但是输出了apple pear
另外,当我们输出 fruits.basket 和 anotherFruits.basket 的值时,前者输出null,而后者输出了apple pear,也就是说 eat() 函数确实被 anotherfruits 调用了,而不是被fruits调用
例二
1 var fruits = [ 2 { one: "coconut", weight: "666kg" }, 3 { two: "chestnut", weight: "888kg" } 4 ] 5 6 var fruit = { 7 fruits: [{ one: "apple", weight: "999kg" }], 8 eat: function () { 9 console.log(this.fruits[0].one + " " + this.fruits[0].weight); 10 } 11 } 12 var fresh = fruit.eat; 13 fresh()
该例中,我们定义了一个全局变量fruits,并且在对象 fruit中也定义了同名变量,接着将 eat() 的方法赋给变量 fresh,然后调用它,该变量属于全局变量,由于上下文的关系,this指向的是全局变量fruits而不是局部变量
我们可以使用bind解决这个问题。
1 var fruits = [ 2 { one: "coconut", weight: "666kg" }, 3 { two: "chestnut", weight: "888kg" } 4 ] 5 6 var fruit = { 7 fruits: [{ one: "apple", weight: "999kg" }], 8 eat: function () { 9 console.log(this.fruits[0].one + " " + this.fruits[0].weight); 10 } 11 } 12 var fresh = fruit.eat.bind(fruit); 13 fresh()
bind改变了this的指向,使变量fresh指向局部变量fruits,也就是说,this的指向取决于fruit的上下文环境
例三
var fruit = { fruits: [ { name: "cococnut", weight: "666g" }, { name: "chestnut", weight: "888g" }, { name: "apple", weight: "999g" } ], basket:"fashioning", eat: function () { this.fruits.forEach(function(fruit){ console.log(fruit.name + " " + this.basket); }) } } fruit.eat(); //cococnut undefined //chestnut undefined //apple undefined
在以上代码中,eat()使用 forEach 迭代数组 fruits,每次迭代都产生一个没有上下文的匿名函数,这类定义在函数内部的函数,称之为闭包(closure),闭包在JS中非常重要,且广泛使用。
另一个重要的概念是作用域(scope)。定义在函数内部的变量不能访问其作用域以外的变量和属性;匿名函数中的this不能访问外部作用域,以至于this只能指向全局对象。该例中,全局对象中没有定义this要访问的属性basket,因此输出undefined。
以上问题的解决方法是:我们可以在匿名函数外为this赋值,然后再函数内使用
var fruit = { fruits: [ { name: "cococnut", weight: "666g" }, { name: "chestnut", weight: "888g" }, { name: "apple", weight: "999g" } ], basket:"fashioning", eat: function () { var self = this this.fruits.forEach(function(fruit){ console.log(fruit.name + " " + self.basket); }) } } fruit.eat(); // cococnut fashioning // chestnut fashioning // apple fashioning
将this赋给变量self,并代替函数体内的this,输出期望结果
例四
var fruit = { name: "fruits", basket: "woody", eat: function (fruits) { fruits.forEach(function (fresh) { console.log(fresh + " " + this.basket); }) } } fruit.eat(['coconut', 'chestnut', 'apple']);
当无法使用 this 进行访问时,可以使用变量 self 来保存它,在本例中,也可以使用箭头函数来解决
var fruit = { name: "delicious", basket: "woody", eat: function (fruits) { fruits.forEach((fresh) => { console.log(fresh + " " + this.basket); }) } } fruit.eat(['coconut', 'chestnut', 'apple']); // coconut woody // chestnut woody // apple woody
可以看出,在 forEach()中使用箭头函数就可以解决问题,而不是进行绑定或暂存this。这里由于箭头函数绑定了上下文,this实际上指向原始上下文或原始对象、
例五
1 var fruitOne = { 2 name: "coconut", 3 basket: "woody", 4 eat: function () { 5 console.log(this.name + " " + this.basket); 6 } 7 } 8 var fruitTwo = { 9 name: "chestnut", 10 basket: "plastic", 11 eat: function (callback) { 12 console.log(this.name + " " + this.basket); 13 callback(); 14 } 15 } 16 fruitTwo.eat(fruitOne.eat); 17 18 //chestnut plastic 19 //undefined
上述代码中定义了两个相同的对象,但其中一个包含回调函数,回调函数将作为参数传入另一个函数,然后通过外部函数调用来完成某种操作。
该代码中对象 fruitTwo 的 eat 方法包含一个回调函数,并在方法中进行直接调用。当将 fruitOne 作为参数调用 fruitTwo.eat()时,输出 chestnut plastic 和 undefined。
结果出乎意料,实际上,fruitOne .eat只是作为参数传入,而不是由 fruitTwo对象调用。换句话说,回调函数调用了对象 fruitOne .eat的方法,却把this绑定到了全局作用域上,证明如下例:
var name="pear" var basket="steel" var fruitOne = { name: "coconut", basket: "woody", eat: function () { console.log(this.name + " " + this.basket); } } var fruitTwo = { name: "chestnut", basket: "plastic", eat: function (callback) { console.log(this.name + " " + this.basket); callback(); } } fruitTwo.eat(fruitOne.eat); // chestnut plastic // pear steel
显而易见,回调函数中输出了全局变量name和basket,再次证明了this指向全局对象,为了得到期望结果,我们将使用bind()将 car 强制绑定到回调函数中,如下:
var name="pear" var basket="steel" var fruitOne = { name: "coconut", basket: "woody", eat: function () { console.log(this.name + " " + this.basket); } } var fruitTwo = { name: "chestnut", basket: "plastic", eat: function (callback) { console.log(this.name + " " + this.basket); callback(); } } fruitTwo.eat(fruitOne.eat.bind(fruitOne)); // chestnut plastic // coconut woody