《你不知道的JavaScript》-- 精读(六)

栏目: JavaScript · 发布时间: 5年前

内容简介:这段代码可以在不同的上下文对象(me和you)中重复使用函数identify()和speak(),不用针对每个对象编写不同版本的函数。如果不使用this,那就需要给identify()和speak()显式传入一个上下文对象。然而,this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。
function identify(){
    return this.name.toUpperCase();
}
function speak(){
    var greeting = "Hello, I'm " + identify.call(this);
    console.log(greeting);
}

var me = {
    name: "Kyle"
}
var you = {
    name: "Reader"
}

identify.call(me); // KYLE
identify.call(you); // READER

speak.call(me); // Hello,我是KYLE
speak.call(you); // Hello,我是READER
复制代码

这段代码可以在不同的上下文对象(me和you)中重复使用函数identify()和speak(),不用针对每个对象编写不同版本的函数。

如果不使用this,那就需要给identify()和speak()显式传入一个上下文对象。

function identify(context){
    return context.name.toUppperCase();
}
function speak(context){
    var greeting = "Hello, I'm " + identify(context);
    console.log(greeting);
}
identify(you); // READER
speak(me); // hello,I'm + identify(context)
复制代码

然而,this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。

随着你的使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用this则不会这样。

2.关于this的误解

2.1 指向自身

function foo(num){
    console.log("foo:" + num);
    // 记录foo被调用的次数
    this.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 10; i++){
    if (i > 5){
        foo(i);
    }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// foo被调用了多少次?
console.log(foo.count); // 0 -- 什么?!
复制代码

执行foo.count = 0时,的确向函数对象foo添加了一个属性count。但是函数内部代码this.count中的this并不是指向那个函数对象,所以虽然属性名相同,根对象却并不相同,困惑随之产生。

实际上,如果深入探索,就会发现这段代码无意中创建了一个全局变量count,其值为NaN。

遇到这样的问题,大多数人会选择下面的解决方案,创建另一个带有count属性的对象。

function foo(num){
    console.log("foo: " + num);
    // 记录foo被调用的次数
    data.count++
}
var data = {
    count: 0
}

var i;
for (i = 0; i < 10; i++){
   if (i > 5){
   foo(i);
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// foo被调用了多少次?
console.log(data.count);
复制代码

上面使用词法作用域“解决”了问题,但是忽视了真正的问题--无法理解this的含义和工作原理。

如果要从函数对象内部引用它自身,那只使用this是不够的。一般来说你需要通过一个指向函数对象的词法标识符(变量)来引用它。

思考下面的这两个函数

function foo(){
    foo.count = 4; // foo指向它自身
}

setTimeout(function (){
    // 匿名(没有名字的)函数无法指向自身
},10)
复制代码

第一个函数被称为具名函数,在它内部可以使用foo来引用自身。

第二个例子中,传入setTimeout(...)的回调函数没有名称标识符(这种函数被称为匿名函数),因此无法从函数内部引用自身。

所以,对于我们的例子来说,另一种解决方法是使用foo标识符替代this来引用函数对象:

function foo(num){
    console.log("foo:" + num);
    // 记录foo被调用的次数
    foo.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 10; i++){
    if (i > 5){
        foo(i);
    }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// foo被调用了多少次?
console.log(foo.count); // 4
复制代码

然而,这种方法同样回避了this的问题,并且完全依赖于变量foo的词法作用域。

另一种方法是强制this指向foo函数对象:

function foo(num){
    console.log("foo:" + num);
    // 记录foo被调用的次数
    // 注意,在当前的调用方式下,this确实指向foo
    this.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 10; i++){
    if (i > 5){
        foo.call(foo,i);
    }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// foo被调用了多少次?
console.log(foo.count); // 4
复制代码

2.2 它的作用域

第二种常见的误解是,this指向函数的作用域。这个问题有点复杂,因为在某种情况下它是正确的,但是在其他情况下它却是错误的。

this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过JavaScript代码访问,它存在于引擎内部。

function foo(){
    var a = 2;
    this.bar();
}
function bar(){
    console.log(this.a);
}

foo(); // ReferenceError: a is not defined
复制代码

首先,这段代码试图通过this.bar()来引用bar()函数。这样调用能成功纯属意外,我们之后会解释原因。调用bar()最自然的方法是省略前面的this,直接使用词法引用标识符。

此外,编写这段代码的开发者还试图使用this联通foo()和bar()的词法作用域,从而让bar()可以访问foo()作用域里的变量a。这是不可能实现的,使用this不可能在词法作用域中查到什么。

每当你想要把this和词法作用域的查找混合使用时,一定要提醒自己,这是无法实现的。

3 this到底是什么

this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到。

总结

this既不指向自身也不指向函数的词法作用域。

this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

巴拉巴拉

关于懒

我本来想写的是关于习惯,后来想想还是不要美化自己了,其实就是懒,也没必要找借口,这个的起因是提交代码时不做确认,其实很多次我都做了,但是一遇到忙,或者提交的文件确实多的时候,我就懒得去检查了,这样就自然而然会导致很多问题,比如,之前就犯过API文档的接口名和实际的接口名不一致而被怼的情况,被教育一番,自己也很是羞愧,写了纸条提醒一定不要再犯,但是吧,还是那句话,忙起来就忘了,这也是我想把它归到习惯的原因,这次又是同样的问题,直接把代码粘贴过来改了一下,也没有检查,就提交了,自然又被批评了一番,心中的懊悔自不必说,意识到了自己需要改正的两个地方,1.提交的东西要严谨,2.尽量不粘贴代码,这次应该肯定不会因为什么就忘了,因为教训是惨痛的!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

爆款:如何打造超级IP

爆款:如何打造超级IP

【美】安妮塔•埃尔伯斯 / 杨雨 / 中信出版社 / 2016-1-10 / 49

哈佛商学院IP运营与产品管理方法论第一书,翻转长尾理论的重要著作! 电影大片、当红炸子鸡、百万畅销书背后的运营逻辑是什么? 《五十度灰》、Lady Gaga、维多利亚的秘密有何共同秘密? 漫威如何将蜘蛛侠、X战警、绿巨人打造成金矿? 皇马如何打造体育IP,一跃成为全球收 入最高的足球俱乐部? 爆款策略如何运用于电影、电视、音乐、出版、体育与商业各领域? ----......一起来看看 《爆款:如何打造超级IP》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

html转js在线工具
html转js在线工具

html转js在线工具