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

 

posted @ 2019-03-28 11:33  Rocket__Ax  阅读(115)  评论(0编辑  收藏  举报