Javascript 进阶 5-8 this:call, apply, bind 与 严谨模式

这篇文章要来介绍三种会影响 this 的方法

这是我们一开始的程式码

var myName = '真心镇大冒险';        var family = {    myName: '小明家'};function fn (para1, para2) {    console.log(this, para1, para2);}

那么我们要介绍的是 call, apply, bind 这三种方法,来改变 this 的指向

这三个的方法观念都很相近,只是呼叫方法上不太一样,我们来看看上一个章节我们提到的 简易呼叫(Simple Call),利用这个 fn 的函式印出来的结果是如何?

var myName = '真心镇大冒险';        var family = {    myName: '小明家'};function fn (para1, para2) {    console.log(this, para1, para2);}fn(1, 2); // 简易呼叫(Simple Call)

http://img2.58codes.com/2024/20121770c1OXr4vxAo.png

我们可以看到运用 简易呼叫(Simple Call) 的方式, this 的指向当然是指向 Window 物件。并且传入的参数是 1 跟 2,这个应该没甚么问题。

call

fn.call(family, 1, 2);// fn.call(要让 this 指向的对象, 参数1, 参数2);

这边再要执行的function后面接上 .call(),并且第一个参数是给予要让前面的 function 执行环境中的 this 要指向哪个对象,通常是给予物件型别的资料,再来后面就是依据传入该 function要传入的参数。

所以结果会是

http://img2.58codes.com/2024/20121770PSuL0LZkf7.png

那么在使用 call 的时候要特别注意,他是立刻执行这个函式,跟我们利用简易呼叫(Simple Call)的方式有点像,主要的差别就是可以透过 call 改变 this 的指向。

apply

apply 的跟 call 很像,只是传入参数给 function 的形式不太一样

fn.apply(family, [3, 4]);// fn.apply(要让 this 指向的对象, [参数1, 参数2]);

使用 apply 方法的时候,要传入的参数必须包在一个阵列里面,当作第二个参数传入。

同样也是立即执行,第一个参数也是用来改变执行环境的 this 指向。

http://img2.58codes.com/2024/20121770vADku7QHJ0.png

bind

bind的方法跟call, apply的主要差异在于,他不会立刻执行前面的 function,因此要使用bind的方法的时候呢,必须先做一些处理。

var fn2 = fn.bind(family, '小明', '杰伦');fn2();

先把 fn.bind(family, '小明', '杰伦') 的内容用变数盛装以后,再利用执行函式的方式执行这个变数。

那么在执行的过程中,就会自动替换 function 的 this 指向。

那么在 fn2() 的小括号中带入参数的话会怎么样吗?

答案是不会,因为我们一开始在定义 fn2 的时候就已经定义好要带入的参数了。

但是如果一开始没有定义好带入的参数的话呢?

var fn2 = fn.bind(family, '小明');fn2(1, 2);

这样的话,少了一个参数,但我又在fn2的小括号中带入1跟2的参数,那么会用哪个参数呢?

http://img2.58codes.com/2024/20121770UFlYsylLBB.png

答案是会从 fn2 传入的第一个参数当作是 fn 的第二的参数,依序补足不够的参数。

另外一个要注意的是,虽然 fn2() 这样的形式是 简易呼叫(Simple Call),但是 fn 的 this 在一开始的时候就被指定了,所以这边就算是用简易呼叫(Simple Call)的方式, fn 的this 还是会被 bind 绑定给指定的物件。

进阶观念

到目前为止,我们知道可以透过 call, apply, bind 来改变 this 的指向。

那么如果是下面这样的状况,会发生甚么事情呢?

fn.call(1, '小明', '杰伦');

http://img2.58codes.com/2024/20121770UaO5jzock3.png

如果是传入其他非物件型别的原始型别资料,会自动被转换成建构式方式的物件型别。

fn.call('文字', '小明', '杰伦');

http://img2.58codes.com/2024/20121770AEZbT6Wv84.png

我们再将 fn 多加上一个 typeof(this)

var myName = '真心镇大冒险';        var family = {    myName: '小明家'};function fn (para1, para2) {    console.log(this, typeof(this), para1, para2);}

存档之后的结果就是

http://img2.58codes.com/2024/20121770FahHSsuzMJ.png

那我们再来传一次不同的值看看

fn.call(undefined, '小明', '杰伦');

http://img2.58codes.com/2024/20121770Ar0yhNrRfb.png

没想到传入了 undefined,居然会直接被指向到 window?!?!?1

为甚么呢?我们来看一下 MDN的文件 怎么说:

http://img2.58codes.com/2024/20121770AW8DjMTcOA.png

也就是说,如果再 非严格模式 的状态下,就会将传入的非物件型别资料封装,也就是利用建构式封装。

并且如果传入的是 null undefined的话,就会被置换成是全域变数。

喔喔~这样应该就能够了解为什么刚刚传入的是 undefined。

但后来 this 会指向为 window了。

但到底甚么是严格模式阿???

严格模式

http://img2.58codes.com/2024/20121770tHyxEIu7LL.png

因为 Javascript 是相对宽鬆的程式,因此有很多不严谨的程式码也能够运行,但在维护上就会相对麻烦,并且不容易除错。

在 ES5 之后就提供了开发者这种语法受限的模式,那么这个模式下呢就有以下这些特点

http://img2.58codes.com/2024/20121770u3bOxwkaCC.png

严格模式呢,只要在程式码中加入 'use strict' 的字串就可以。

主要是因为只撰写 'use strict' 的字串 是属于一种表达式,那么这个程式码不会影响不支援严格模式的浏览器。

并且可以加在特定的函式内进行严格模式,也就是说只有在特定的大括号内中执行的程式码才会套用严格模式。当然如果在全域的环境使用的话,你所有的程式码都会进入严格模式喔!

(function () {    'use strict';    // 这个执行环境进入了严格模式})();

那么在严格模式中,透过抛出错误的方式来提醒开发者一些安静的错误。也可以避免掉一些开发的不好习惯,同时也有利于侦错。

http://img2.58codes.com/2024/20121770m3edkNlHoS.png

其中一个例子就是,如果在严格模式下,没有使用 var let const 等宣告变数的方式进行宣告的话,他会直接跳错。

在严格模式下呢,他不允许你直接对变数赋予值,他会要你先宣告这个变数之后,再对变数赋予值。

那么其他更详细,有关严格模式的内容,可以参考 MDN的文件

不过要特别注意的是,在严格模式的状态下,每个浏览器的结果可能会有不同,所以有时候文件跟实际的状况有出入也是属于正常的。要特别有心理準备~XDD

那么接着我们再来看一下严格模式的其他例子吧~

function callStrict (para1, para2) {    'use strict';    console.log(this, typeof(this), para1, para2);}callStrict.call(1, '小明', '杰伦');

这样会发生甚么事情呢?

http://img2.58codes.com/2024/2012177019yWf6VkSR.png

可以看到在严格模式下,this 的指向就是我们透过 call 所传入的 数字1。

并且没有被封装成建构式的形式!

那再来看看传入 undefined 给大家看看

callStrict.call(undefined, '小明', '杰伦');

http://img2.58codes.com/2024/201217709Nce6zFEQQ.png

原本我们再非严格模式(sloppy mode)下,传入 undefined 的话就会将 this 指向全域的window 物件。

那么再严格模式下,传入 undefined 的时候就还是会维持 undefined。

那么我如果简易呼叫 callStrict 的话会怎么样呢?

callStrict('小明', '杰伦');

http://img2.58codes.com/2024/20121770jHOFdxi2nH.png

那么为什么会这样呢?

其实你使用简易呼叫的时候,就等于是直接套用call这个方式进行呼叫。只是我们第一个参数没有传入,所以是 undefined。

那么如果再严格模式下的话,当然就会是 undefined;如果是非严格模式(sloppy mode)下,虽然会自动将 this 自动指向到 window 物件,可是它的本质其实是 undefined。

这也是为什么前面说的,在使用简易呼叫的时候,尽量不要去调用他的 this, 因为它的本质其实是 undefined。

好~那么这篇文章就介绍到这边,没问题就往下继续看下去吧~!汪汪


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章