菜单

JavaScript深远之call和apply的模仿完结(转)

2019年4月5日 - 皇家前端

JavaScript 深远之call和apply的画虎不成反类犬达成

2017/05/25 · JavaScript
· apply,
call

原稿出处: 冴羽   

因此call和apply的模仿实现,带你爆料call和apply改变this的本色

JavaScript 深切之bind的优孟衣冠达成

2017/05/26 · JavaScript
· bind

原来的书文出处: 冴羽   

this:this的指向在函数定义的时候是规定不了的,唯有函数执行的时候才能鲜明this到底指向什么人,实际上this的最后指向的是尤其调用它的靶子

call

一句话介绍 call:

call() 方法在选用二个点名的 this
值和几何个内定的参数值的前提下调用有个别函数或措施。

举个例证:

var foo = { value: 1 }; function bar() { console.log(this.value); }
bar.call(foo); // 1

1
2
3
4
5
6
7
8
9
var foo = {
    value: 1
};
 
function bar() {
    console.log(this.value);
}
 
bar.call(foo); // 1

注意两点:

  1. call 改变了 this 的指向,指向到 foo
  2. bar 函数执行了

call
一句话介绍 call:
call() 方法在动用三个点名的 this
值和多少个钦定的参数值的前提下调用有些函数或方法。

bind

一句话介绍 bind:

bind() 方法会创制1个新函数。当以此新函数被调用时,bind()
的率先个参数将作为它运转时的
this,之后的1种类参数将会在传递的实参前流传作为它的参数。(来自于 MDN
)

透过大家能够率先得出 bind 函数的七个特征:

  1. 归来3个函数
  2. 能够流传参数

一.非构造函数版this

依样葫芦达成率先步

那正是说大家该怎么模拟实现那七个功效呢?

试想当调用 call 的时候,把 foo 对象改造成如下:

var foo = { value: 1, bar: function() { console.log(this.value) } };
foo.bar(); // 1

1
2
3
4
5
6
7
8
var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
};
 
foo.bar(); // 1

其一时半刻候 this 就对准了 foo,是还是不是很粗略吗?

但是这么却给 foo 对象自笔者添加了1性情子,那可丰裕呀!

而是也不用担心,我们用 delete 再删除它不就好了~

从而大家模拟的手续能够分为:

  1. 将函数设为对象的习性
  2. 实践该函数
  3. 删去该函数

上述个例子为例,就是:

// 第一步 foo.fn = bar // 第二步 foo.fn() // 第三步 delete foo.fn

1
2
3
4
5
6
// 第一步
foo.fn = bar
// 第二步
foo.fn()
// 第三步
delete foo.fn

fn 是目的的属性名,反正最终也要去除它,所以起成怎么样都无所谓。

依照这么些思路,我们得以品味着去写第2版的 call2 函数:

// 第2版 Function.prototype.call2 = function(context) { //
首先要取得调用call的函数,用this能够博得 context.fn = this;
context.fn(); delete context.fn; } // 测试一下 var foo = { value: 一 };
function bar() { console.log(this.value); } bar.call贰(foo); // 一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 第一版
Function.prototype.call2 = function(context) {
    // 首先要获取调用call的函数,用this可以获取
    context.fn = this;
    context.fn();
    delete context.fn;
}
 
// 测试一下
var foo = {
    value: 1
};
 
function bar() {
    console.log(this.value);
}
 
bar.call2(foo); // 1

碰巧能够打字与印刷 壹 哎!是否很高兴!(~ ̄▽ ̄)~

举个例子:
var foo = { value: 1};function bar() {
console.log(this.value);}bar.call(foo); // 1

重临函数的模拟达成

从第2个天性开头,我们举个例子:

var foo = { value: 1 }; function bar() { console.log(this.value); } //
重回了3个函数 var bindFoo = bar.bind(foo); bindFoo(); // 一

1
2
3
4
5
6
7
8
9
10
11
12
var foo = {
    value: 1
};
 
function bar() {
    console.log(this.value);
}
 
// 返回了一个函数
var bindFoo = bar.bind(foo);
 
bindFoo(); // 1

至于钦命 this 的针对性,大家可以利用 call 大概 apply 贯彻,关于 call 和
apply
的照猫画虎实现,能够查阅《JavaScript深入之call和apply的如法泡制达成》。大家来写第2版的代码:

// 第一版 Function.prototype.bind2 = function (context) { var self =
this; return function () { self.apply(context); } }

1
2
3
4
5
6
7
8
// 第一版
Function.prototype.bind2 = function (context) {
    var self = this;
    return function () {
        self.apply(context);
    }
 
}

1.1 function a(){

依样画葫芦达成第二步

最一起首也讲了,call 函数还是能给定参数执行函数。举个例子:

var foo = { value: 1 }; function bar(name, age) { console.log(name)
console.log(age) console.log(this.value); } bar.call(foo, ‘kevin’, 18);
// kevin // 18 // 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var foo = {
    value: 1
};
 
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}
 
bar.call(foo, ‘kevin’, 18);
// kevin
// 18
// 1

注意:传入的参数并不显著,那可怎么办?

不急,我们得以从 Arguments
对象中取值,取出第二个到最终1个参数,然后放到多少个数组里。

譬如说那样:

// 以上个例子为例,此时的arguments为: // arguments = { // 0: foo, // 1:
‘kevin’, // 贰: 1八, // length: 三 // } //
因为arguments是类数组对象,所以能够用for循环 var args = []; for(var i
= 1, len = arguments.length; i len; i++) { args.push(‘arguments[‘ + i +
‘]’); } // 执行后 args为 [foo, ‘kevin’, 18]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 以上个例子为例,此时的arguments为:
// arguments = {
//      0: foo,
//      1: ‘kevin’,
//      2: 18,
//      length: 3
// }
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for(var i = 1, len = arguments.length; i  len; i++) {
    args.push(‘arguments[‘ + i + ‘]’);
}
 
// 执行后 args为 [foo, ‘kevin’, 18]

不定长的参数难点一蹴而就了,大家随后要把这几个参数数组放到要实践的函数的参数里面去。

// 将数组里的因素作为多个参数放进函数的形参里 context.fn(args.join(‘,’))
// (O_o)?? // 那几个点子肯定是可怜的啊!!!

1
2
3
4
// 将数组里的元素作为多个参数放进函数的形参里
context.fn(args.join(‘,’))
// (O_o)??
// 这个方法肯定是不行的啦!!!

可能有人想到用 ES陆 的办法,不过 call 是 ES3 的点子,大家为了模仿完结3个ES3 的主意,要用到ES陆的主意,好像……,嗯,也能够啊。但是大家此番用 eval
方法拼成二个函数,类似于如此:

eval(‘context.fn(‘ + args +’)’)

1
eval(‘context.fn(‘ + args +’)’)

此间 args 会自动调用 Array.toString() 这一个办法。

因此大家的第二版击败了八个大题材,代码如下:

// 第二版 Function.prototype.call2 = function(context) { context.fn =
this; var args = []; for(var i = 1, len = arguments.length; i len;
i++) { args.push(‘arguments[‘ + i + ‘]’); } eval(‘context.fn(‘ + args
+’)’); delete context.fn; } // 测试一下 var foo = { value: 一 }; function
bar(name, age) { console.log(name) console.log(age)
console.log(this.value); } bar.call二(foo, ‘kevin’, 1捌); // kevin // 18
// 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 第二版
Function.prototype.call2 = function(context) {
    context.fn = this;
    var args = [];
    for(var i = 1, len = arguments.length; i  len; i++) {
        args.push(‘arguments[‘ + i + ‘]’);
    }
    eval(‘context.fn(‘ + args +’)’);
    delete context.fn;
}
 
// 测试一下
var foo = {
    value: 1
};
 
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}
 
bar.call2(foo, ‘kevin’, 18);
// kevin
// 18
// 1

(๑•̀ㅂ•́)و✧

留意两点:
call 改变了 this 的指向,指向到 foo
bar 函数执行了

传参的模仿达成

接下去看第2点,能够流传参数。那一个就有点令人费解了,小编在 bind
的时候,是或不是能够传参呢?笔者在推行 bind
再次回到的函数的时候,可以还是不可以传参呢?让大家看个例子:

var foo = { value: 1 }; function bar(name, age) {
console.log(this.value); console.log(name); console.log(age); } var
bindFoo = bar.bind(foo, ‘daisy’); bindFoo(’18’); // 1 // daisy // 18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var foo = {
    value: 1
};
 
function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
 
}
 
var bindFoo = bar.bind(foo, ‘daisy’);
bindFoo(’18’);
// 1
// daisy
// 18

函数须要传 name 和 age 七个参数,竟然仍是能够在 bind 的时候,只传三个name,在执行回来的函数的时候,再传另1个参数 age!

那可如何做?不急,大家用 arguments 举办拍卖:

// 第3版 Function.prototype.bind二 = function (context) { var self =
this; // 获取bind2函数从第二个参数到终极四个参数 var args =
Array.prototype.slice.call(arguments, 1); return function () { //
那一年的arguments是指bind重回的函数字传送入的参数 var bindArgs =
Array.prototype.slice.call(arguments); self.apply(context,
args.concat(bindArgs)); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 第二版
Function.prototype.bind2 = function (context) {
 
    var self = this;
    // 获取bind2函数从第二个参数到最后一个参数
    var args = Array.prototype.slice.call(arguments, 1);
 
    return function () {
        // 这个时候的arguments是指bind返回的函数传入的参数
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(context, args.concat(bindArgs));
    }
 
}

    var user =”追梦子”;

宪章完毕第三步

上行下效代码已经完毕 五分之四,还有七个小点要专注:

一.this 参数能够传 null,当为 null 的时候,视为指向 window

举个例证:

var value = 1; function bar() { console.log(this.value); }
bar.call(null); // 1

1
2
3
4
5
6
7
var value = 1;
 
function bar() {
    console.log(this.value);
}
 
bar.call(null); // 1

固然如此那个例子本人不利用 call,结果还是一样。

二.函数是能够有重回值的!

举个例子:

var obj = { value: 1 } function bar(name, age) { return { value:
this.value, name: name, age: age } } console.log(bar.call(obj, ‘kevin’,
18)); // Object { // value: 1, // name: ‘kevin’, // age: 18 // }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var obj = {
    value: 1
}
 
function bar(name, age) {
    return {
        value: this.value,
        name: name,
        age: age
    }
}
 
console.log(bar.call(obj, ‘kevin’, 18));
// Object {
//    value: 1,
//    name: ‘kevin’,
//    age: 18
// }

不过都很好化解,让我们直接看第贰版也正是最后一版的代码:

// 第三版 Function.prototype.call2 = function (context) { var context =
context || window; context.fn = this; var args = []; for(var i = 1,
len = arguments.length; i len; i++) { args.push(‘arguments[‘ + i +
‘]’); } var result = eval(‘context.fn(‘ + args +’)’); delete context.fn
return result; } // 测试一下 var value = 二; var obj = { value: 一 }
function bar(name, age) { console.log(this.value); return { value:
this.value, name: name, age: age } } bar.call(null); // 二console.log(bar.call二(obj, ‘kevin’, 1八)); // 壹 // Object { // value: 壹,
// name: ‘kevin’, // age: 1捌 // }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 第三版
Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;
 
    var args = [];
    for(var i = 1, len = arguments.length; i  len; i++) {
        args.push(‘arguments[‘ + i + ‘]’);
    }
 
    var result = eval(‘context.fn(‘ + args +’)’);
 
    delete context.fn
    return result;
}
 
// 测试一下
var value = 2;
 
var obj = {
    value: 1
}
 
function bar(name, age) {
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
}
 
bar.call(null); // 2
 
console.log(bar.call2(obj, ‘kevin’, 18));
// 1
// Object {
//    value: 1,
//    name: ‘kevin’,
//    age: 18
// }

到此,大家成功了 call 的因循古板完成,给本身多少个赞 b( ̄▽ ̄)d

仿照达成率先步
那么大家该怎么模拟达成那七个成效啊?
试想当调用 call 的时候,把 foo 对象改造成如下:
var foo = { value: 1, bar: function() { console.log(this.value)
}};foo.bar(); // 1

构造函数效果的模拟实现

成就了这两点,最难的有的到啊!因为 bind 还有四个特色,便是

三个绑定函数也能利用new操作符创设对象:那种表现就如把原函数当成构造器。提供的
this 值被忽视,同时调用时的参数被提需求模拟函数。

也正是说当 bind 再次回到的函数作为构造函数的时候,bind 时钦定的 this
值会失效,但传播的参数依然奏效。举个例子:

var value = 2; var foo = { value: 1 }; function bar(name, age) {
this.habit = ‘shopping’; console.log(this.value); console.log(name);
console.log(age); } bar.prototype.friend = ‘kevin’; var bindFoo =
bar.bind(foo, ‘daisy’); var obj = new bindFoo(’18’); // undefined //
daisy // 18 console.log(obj.habit); console.log(obj.friend); // shopping
// kevin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var value = 2;
 
var foo = {
    value: 1
};
 
function bar(name, age) {
    this.habit = ‘shopping’;
    console.log(this.value);
    console.log(name);
    console.log(age);
}
 
bar.prototype.friend = ‘kevin’;
 
var bindFoo = bar.bind(foo, ‘daisy’);
 
var obj = new bindFoo(’18’);
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin

留神:尽管在全局和 foo 中都宣示了 value 值,最终依旧再次回到了
undefind,表达绑定的 this 失效了,假如大家了然 new
的一步一趋实现,就会清楚那个时候的 this 已经针对了 obj。

(哈哈,笔者那是为我的下1篇小说《JavaScript深远种类之new的上行下效实现》打广告)。

故而大家得以由此改动重临的函数的原型来贯彻,让大家写一下:

// 第二版 Function.prototype.bind二 = function (context) { var self =
this; var args = Array.prototype.slice.call(arguments, 1); var fbound =
function () { var bindArgs = Array.prototype.slice.call(arguments); //
当作为构造函数时,this 指向实例,self 指向绑定函数,因为上边一句
`fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为
绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this
指向实例。 // 当作为普通函数时,this 指向 window,self
指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的
context。 self.apply(this instanceof self ? this : context,
args.concat(bindArgs)); } // 修改再次回到函数的 prototype 为绑定函数的
prototype,实例就足以继续函数的原型中的值 fbound.prototype =
this.prototype; return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 第三版
Function.prototype.bind2 = function (context) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
 
    var fbound = function () {
 
        var bindArgs = Array.prototype.slice.call(arguments);
        // 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句 `fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
        // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 context。
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值
    fbound.prototype = this.prototype;
    return fbound;
}

如果对原型链稍有疑惑,能够查阅《JavaScript深刻之从原型到原型链》。

    console.log(this.user);//undefined

apply的效仿完毕

apply 的落到实处跟 call 类似,在此间直接给代码,代码来自于今日头条 @郑航的兑现:

Function.prototype.apply = function (context, arr) { var context =
Object(context) || window; context.fn = this; var result; if (!arr) {
result = context.fn(); } else { var args = []; for (var i = 0, len =
arr.length; i len; i++) { args.push(‘arr[‘ + i + ‘]’); } result =
eval(‘context.fn(‘ + args + ‘)’) } delete context.fn return result; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;
 
    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i  len; i++) {
            args.push(‘arr[‘ + i + ‘]’);
        }
        result = eval(‘context.fn(‘ + args + ‘)’)
    }
 
    delete context.fn
    return result;
}

本条时候 this 就对准了 foo,是或不是很简短吗?
不过这么却给 foo 对象自作者添加了3本性质,那可特别啊!
然则也不用担心,大家用 delete 再删除它不就好了~
由此大家模拟的步骤能够分成:
将函数设为对象的习性
执行该函数
删去该函数

构造函数效果的优化达成

可是在这一个写法中,大家一向将 fbound.prototype =
this.prototype,我们一贯修改 fbound.prototype 的时候,也会直接修改函数的
prototype。那年,大家得以经过四个空函数来展开转向:

// 第四版 Function.prototype.bind2 = function (context) { var self =
this; var args = Array.prototype.slice.call(arguments, 1); var fNOP =
function () {}; var fbound = function () { var bindArgs =
Array.prototype.slice.call(arguments); self.apply(this instanceof self ?
this : context, args.concat(bindArgs)); } fNOP.prototype =
this.prototype; fbound.prototype = new fNOP(); return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 第四版
Function.prototype.bind2 = function (context) {
 
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
 
    var fNOP = function () {};
 
    var fbound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
    return fbound;
 
}

到此甘休,大的标题都早已解决,给协调三个赞!o( ̄▽ ̄)d

    console.log(this);//Window

重中之重参考

新浪难题 不能够应用call、apply、bind,怎样用 js 实现 call 大概 apply
的功能?

如上个例证为例,正是:
// 第一步foo.fn = bar// 第二步foo.fn()// 第三步delete foo.fn

四个不奇怪

接下去处理些没反常:

一.apply 那段代码跟 MDN 上的稍有例外

在 MDN 粤语版讲 bind 的模仿完成时,apply 那里的代码是:

self.apply(this instanceof self ? this : context || this,
args.concat(bindArgs))

1
self.apply(this instanceof self ? this : context || this, args.concat(bindArgs))

多了1个有关 context 是还是不是存在的论断,可是那一个是荒谬的!

举个例证:

var value = 2; var foo = { value: 1, bar: bar.bind(null) }; function
bar() { console.log(this.value); } foo.bar() // 2

1
2
3
4
5
6
7
8
9
10
11
var value = 2;
var foo = {
    value: 1,
    bar: bar.bind(null)
};
 
function bar() {
    console.log(this.value);
}
 
foo.bar() // 2

如上代码通常意况下会打字与印刷 二,假设换到了 context || this,那段代码就会打字与印刷1!

之所以那里不该进行 context 的判定,大家查看 MDN
同样内容的英文版,就不设有这些论断!

二.调用 bind 的不是函数怎么办?

老大,大家要报错!

if (typeof this !== “function”) { throw new
Error(“Function.prototype.bind – what is trying to be bound is not
callable”); }

1
2
3
if (typeof this !== "function") {
  throw new Error("Function.prototype.bind – what is trying to be bound is not callable");
}

三.自家要在线上用

那别忘了做个至极:

Function.prototype.bind = Function.prototype.bind || function () { …… };

1
2
3
Function.prototype.bind = Function.prototype.bind || function () {
    ……
};

本来最棒是用es5-shim啦。

}

深深连串

JavaScript深切体系目录地址:。

JavaScript深入连串推断写拾伍篇左右,目的在于帮大家捋顺JavaScript底层知识,重点教学如原型、成效域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难处概念。

只要有错误恐怕不谨慎的地点,请务必给予指正,拾分多谢。借使喜欢或许有所启发,欢迎star,对作者也是一种鞭策。

本系列:

  1. JavaScirpt 深远之从原型到原型链
  2. JavaScript
    深远之词法功能域和动态功效域
  3. JavaScript 深切之实践上下文栈
  4. JavaScript 深切之变量对象
  5. JavaScript 深刻之作用域链
  6. JavaScript 深切之从 ECMAScript 规范解读
    this
  7. JavaScript 深切之实施上下文
  8. JavaScript 深切之闭包
  9. JavaScript 深刻之参数按值传递

    1 赞 收藏
    评论

图片 1

fn 是指标的属性名,反正最终也要去除它,所以起成怎么着都无所谓。
依照那么些思路,我们得以品味着去写第一版的 call2 函数:
// 第二版Function.prototype.call2 = function(context) { //
首先要赢得调用call的函数,用this能够赢得 context.fn = this;
context.fn(); delete context.fn;}// 测试一下var foo = { value:
一};function bar() { console.log(this.value);}bar.call二(foo); // 1

最后代码

由此最末尾的代码就是:

Function.prototype.bind2 = function (context) { if (typeof this !==
“function”) { throw new Error(“Function.prototype.bind – what is trying
to be bound is not callable”); } var self = this; var args =
Array.prototype.slice.call(arguments, 1); var fNOP = function () {}; var
fbound = function () { self.apply(this instanceof self ? this : context,
args.concat(Array.prototype.slice.call(arguments))); } fNOP.prototype =
this.prototype; fbound.prototype = new fNOP(); return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Function.prototype.bind2 = function (context) {
 
    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind – what is trying to be bound is not callable");
    }
 
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};
 
    var fbound = function () {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }
 
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
 
    return fbound;
 
}

a();相当于window.a()所以a指向window

恰巧可以打字与印刷 一 哎!是或不是很心情舒畅!(~ ̄▽ ̄)~
宪章达成第3步
最一初叶也讲了,call 函数还是能给定参数执行函数。举个例子:
var foo = { value: 1};function bar(name, age) { console.log(name)
console.log(age) console.log(this.value);}bar.call(foo, ‘kevin’, 18);//
kevin// 18// 1

深深连串

JavaScript深切体系目录地址:。

JavaScript深刻类别估算写105篇左右,意在帮大家捋顺JavaScript底层知识,重点教学如原型、功能域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难处概念。

假如有不当可能不审慎的地点,请务必给予指正,1二分多谢。倘若喜欢依然具有启发,欢迎star,对小编也是一种鞭策。

本系列:

  1. JavaScirpt 深入之从原型到原型链
  2. JavaScript
    深刻之词法效能域和动态作用域
  3. JavaScript 深刻之实施上下文栈
  4. JavaScript 深切之变量对象
  5. JavaScript 深切之效果域链
  6. JavaScript 深切之从 ECMAScript 规范解读
    this
  7. JavaScript 深入之推行上下文
  8. JavaScript 深远之闭包
  9. JavaScript 长远之参数按值传递
  10. JavaScript
    长远之call和apply的模拟完成

    1 赞 收藏
    评论

图片 2

1.2 var o = {

留神:传入的参数并不明确,那可咋做?
不急,大家能够从 Arguments
对象中取值,取出第二个到最终2个参数,然后放到三个数组里。
诸如那样:
// 以上个例子为例,此时的arguments为:// arguments = {// 0: foo,// 1:
‘kevin’,// 二: 1八,// length: 3// }//
因为arguments是类数组对象,所以能够用for循环var args = [];for(var i =
1, len = arguments.length; i < len; i++) { args.push(‘arguments[‘ +
i + ‘]’);}// 执行后 args为 [foo, ‘kevin’, 18]

    user:”追梦子”,

不定长的参数难题一蹴而就了,我们跟着要把这些参数数组放到要履行的函数的参数里面去。
//
将数组里的因素作为多少个参数放进函数的形参里context.fn(args.join(‘,’))//
(O_o)??// 这么些方法肯定是万分的啦!!!

    fn:function(){

想必有人想到用 ES陆 的法门,但是 call 是 ES3 的诀要,大家为了仿效完毕一个ES3 的章程,要用到ES陆的措施,好像……,嗯,也得以啦。可是我们此次用 eval
方法拼成叁个函数,类似于那般:
eval(‘context.fn(‘ + args +’)’)

        console.log(this.user);//追梦子    

此间 args 会自动调用 Array.toString() 那么些艺术。
故而大家的第一版克制了多少个大标题,代码如下:
// 第二版Function.prototype.call2 = function(context) { context.fn =
this; var args = []; for(var i = 1, len = arguments.length; i <
len; i++) { args.push(‘arguments[‘ + i + ‘]’); } eval(‘context.fn(‘ +
args +’)’); delete context.fn;}// 测试一下var foo = { value: 一};function
bar(name, age) { console.log(name) console.log(age)
console.log(this.value);}bar.call2(foo, ‘kevin’, 1八); // kevin// 18// 1

            }

(๑•̀ㅂ•́)و✧
宪章落成第一步
依样画葫芦代码已经做到 五分之四,还有七个小点要小心:
1.this 参数可以传 null,当为 null 的时候,视为指向 window
举个例子:
var value = 1;function bar() { console.log(this.value);}bar.call(null);
// 1

}

纵然这几个事例本人不选择 call,结果依然一样。
2.函数是足以有重回值的!
举个例证:
var obj = { value: 1}function bar(name, age) { return { value:
this.value, name: name, age: age }}console.log(bar.call(obj, ‘kevin’,
18));// Object {// value: 1,// name: ‘kevin’,// age: 18// }

o.fn(); o调用fn,所以this指向o

可是都很好化解,让我们直接看第3版也正是终极一版的代码:
// 第三版Function.prototype.call2 = function (context) { var context =
context || window; context.fn = this; var args = []; for(var i = 1,
len = arguments.length; i < len; i++) { args.push(‘arguments[‘ + i +
‘]’); } var result = eval(‘context.fn(‘ + args +’)’); delete context.fn
return result;}// 测试一下var value = 贰;var obj = { value: 1}function
bar(name, age) { console.log(this.value); return { value: this.value,
name: name, age: age }}bar.call(null); // 2console.log(bar.call二(obj,
‘kevin’, 1八));// 1// Object {// value: 1,// name: ‘kevin’,// age: 18// }

1.3  var o = {

到此,大家做到了 call 的效仿完结,给本身2个赞 b( ̄▽ ̄)d
apply的停滞不前达成
apply 的贯彻跟 call 类似,在那里向来给代码,代码来自于果壳网@郑航的达成:
Function.prototype.apply = function (context, arr) { var context =
Object(context) || window; context.fn = this; var result; if (!arr) {
result = context.fn(); } else { var args = []; for (var i = 0, len =
arr.length; i < len; i++) { args.push(‘arr[‘ + i + ‘]’); } result =
eval(‘context.fn(‘ + args + ‘)’) } delete context.fn return result;}

    a:10,

浓密类别
JavaScript深切种类目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript浓厚种类臆度写105篇左右,目的在于帮大家捋顺JavaScript底层知识,重点教学如原型、成效域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等困难概念。
万1有不当恐怕不审慎的地点,请务必给予指正,11分多谢。假使喜欢依然具有启发,欢迎star,对笔者也是1种鞭策。

    b:{

        a:12,

        fn:function(){

            console.log(this.a);//undefinedconsole.log(this);//window   
    }

    }

}

var j = o.b.fn;

j(); //此时this指向window

二.构造函数版this

2.1 function Fn(){

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图