JavaScript深刻之call和apply的模仿达成,深入之call和apply的效仿达成

by admin on 2019年3月10日

JavaScript 深切之call和apply的模仿实现

2017/05/25 · JavaScript
· apply,
call

初稿出处: 冴羽   

经过call和apply的效仿达成,带你揭发call和apply改变this的真面目

JavaScript 深切之bind的效仿完毕

2017/05/26 · JavaScript
· bind

原来的文章出处: 冴羽   

this:JavaScript深刻之call和apply的模仿达成,深入之call和apply的效仿达成。JavaScript深刻之call和apply的模仿达成,深入之call和apply的效仿达成。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() 方法会创设3个新函数。当以此新函数被调用时,bind()
的率先个参数将作为它运维时的
this,之后的一种类参数将会在传递的实参前流传作为它的参数。(来自于 MDN
)

通过我们能够率先得出 bind 函数的四个特色:

  1. 回到2个函数
  2. 能够流传参数

1.非构造函数版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 对象自小编添加了2天品质,那可特别啊!

可是也不用担心,大家用 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 是指标的属性名,反正最终也要刨除它,所以起成什么样都不在乎。

据他们说那一个思路,大家能够品尝着去写第叁版的 call2 函数:

// 第2版 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

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

恰巧可以打印 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); } //
重返了二个函数 var bindFoo = bar.bind(foo); bindFoo(); // 1

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的模仿完毕》。我们来写第③版的代码:

// 第一版 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(){

效仿达成第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

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
对象中取值,取出第2个到最后二个参数,然后嵌入三个数组里。

比如说那样:

// 以上个例证为例,此时的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]

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)?? // 那个主意自然是11分的呐!!!

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

唯恐有人想到用 ES6 的方法,不过 call 是 ES3 的法子,我们为了仿效完毕一个ES3 的办法,要用到ES6的点子,好像……,嗯,也足以啦。不过大家此次用 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: 1 }; function
bar(name, age) { console.log(name) console.log(age)
console.log(this.value); } bar.call2(foo, ‘kevin’, 18); // 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 函数执行了

传参的效仿达成

接下去看第贰点,可以流传参数。这一个就有点令人费解了,小编在 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 的时候,只传1个name,在实践回来的函数的时候,再传另3个参数 age!

那可如何是好?不急,大家用 arguments 进行拍卖:

// 第三版 Function.prototype.bind2 = function (context) { var self =
this; // 获取bind2函数从首个参数到最终2个参数 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 =”追梦子”;

宪章完结第3步

模仿代码已经完毕 五分四,还有多个小点要小心:

1.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,结果仍旧一样。

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 // }

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 = 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 // }

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。

(哈哈,小编那是为自家的下一篇文章《JavaScript深远类别之new的模拟落成》打广告)。

据此大家得以经过修改重回的函数的原型来兑现,让大家写一下:

// 第二版 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; }

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。那些时候,大家能够透过1个空函数来展开转账:

// 第四版 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

四个小标题

接下去处理些小难题:

1.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

以上代码正常状态下会打字与印刷 2,若是换到了 context || this,那段代码就会打字与印刷1!

据此那边不应有举办 context 的判断,大家查看 MDN
同样内容的英文版,就不设有那么些论断!

2.调用 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");
}

3.自身要在线上用

那别忘了做个分外:

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 赞 收藏
    评论

必发88 1

fn 是指标的属性名,反正最终也要删减它,所以起成怎么样都无所谓。
据悉这几个思路,大家得以品尝着去写第1版的 call2 函数:
// 第三版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

说到底代码

于是最末尾的代码就是:

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

恰巧能够打字与印刷 1 哎!是还是不是很心满意足!(~ ̄▽ ̄)~
模仿达成第1步
最一起头也讲了,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深切种类测度写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、成效域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等困难概念。

假设有不当只怕不审慎的地点,请务必给予指正,10分多谢。要是喜欢照旧有所启发,欢迎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 赞 收藏
    评论

必发88 2

1.2 var o = {

小心:传入的参数并不鲜明,那可咋做?
不急,大家能够从 Arguments
对象中取值,取出第②个到终极贰个参数,然后放到2个数组里。
比如说那样:
// 以上个例子为例,此时的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]

    user:”追梦子”,

不定长的参数难题消除了,大家跟着要把那些参数数组放到要实施的函数的参数里面去。
//
将数组里的成分作为多个参数放进函数的形参里context.fn(args.join(‘,’))//
(O_o)??// 这一个方法自然是非常的啊!!!

    fn:function(){

想必有人想到用 ES6 的不二法门,不过 call 是 ES3 的格局,大家为了模仿实现3个ES3 的方法,要用到ES6的法子,好像……,嗯,也足以啊。可是大家此次用 eval
方法拼成3个函数,类似于如此:
eval(‘context.fn(‘ + args +’)’)

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

那里 args 会自动调用 Array.toString() 那个方法。
所以大家的第2版制服了四个大题材,代码如下:
// 第二版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

            }

(๑•̀ㅂ•́)و✧
必发88,照猫画虎达成第3步
宪章代码已经到位 十分八,还有多个小点要小心:
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

而是都很好消除,让我们间接看第一版约等于终极一版的代码:
// 第三版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); // 2console.log(bar.call2(obj,
‘kevin’, 18));// 1// Object {// value: 1,// name: ‘kevin’,// age: 18// }

1.3  var o = {

到此,大家成功了 call 的依样葫芦完毕,给协调二个赞 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深远连串估算写十五篇左右,目的在于帮大家捋顺JavaScript底层知识,重点教学如原型、成效域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难题概念。
即使有荒唐可能不审慎的地点,请务必给予指正,11分谢谢。假设喜欢或然持有启发,欢迎star,对笔者也是一种鞭策。

    b:{

        a:12,

        fn:function(){

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

    }

}

var j = o.b.fn;

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

2.构造函数版this

2.1 function Fn(){

    this.user = “追梦子”;

}

var a =new Fn();

console.log(a.user); //追梦子 this指向a对象

2.2 function fn()

    this.user = ‘追梦子’; 

    return {}; //或者:return function(){};

}

var a =new fn; 

console.log(a.user); //undefined
 由于重临了1个目的所以this指向重临的对象而不是a对象

2.3 function fn()

    this.user = ‘追梦子’; 

    return 1; //或者:return undefined;

}

var a =new fn; 

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

3.call和apply

相同点:改变函数内部的this指向

差别点:接收参数方式各异 

3.1 apply(obj,[argArray]), call(obj,arg1,arg2,arg3…)

function add(c,d){ 

     return this.a + this.b + c + d;

}

var s = {a:1, b:2};

console.log(add.call(s,3,4)); // 1+2+3+4 = 10    

console.log(add.apply(s,[5,6])); // 1+2+5+6 = 14

3.2  window.firstName = “Cynthia”;

        window.lastName = “_xie”;

        var myObject = {firstName:’my’, lastName:’Object’};

        functiongetName(){            

                console.log(this.firstName + this.lastName);

        }

        functiongetMessage(sex,age){            

                console.log(this.firstName + this.lastName + ” 性别: ” +
sex + ” age: ” + age );

        }

        getName.call(window); // Cynthia_xie       

         getName.call(myObject); // myObject        

        getName.apply(window); // Cynthia_xie        

        getName.apply(myObject);// myObject        

        getMessage.call(window,”女”,21); //Cynthia_xie 性别: 女 age:
21        

        getMessage.apply(window,[“女”,21]); // Cynthia_xie 性别: 女
age: 21        

        getMessage.call(myObject,”未知”,22); //myObject 性别: 未知 age:
22       

         getMessage.apply(myObject,[“未知”,22]); // myObject 性别:
未知 age: 22

4.Bind

var newFunc = obj1.bind(obj2,2,3)
bind发生了叁个新的函数newFunc,其this指向为obj2

如:var bar=function(a,b){

  console.log(this.x,a,b); 

}

var foo={

    x:3 

var func =  bar.bind(foo);

bar()

func(3,4)

这会儿出口为:3 3 4

假使var func =  bar.bind(foo)改为:var func = 
bar.bind(foo,2,3),那么输出为:3 2 3

5.argument

① 、大家得以借用arguments.length能够来查阅实参和形参的个数是还是不是一致:

function add(a,b){

    var reallyLen = arguments.length;

    console.log(“really arg len”,reallyLen);

    var funcLen = add.length;

    console.log(“func arg len”,funcLen);

}

add(1,2,3,4,5)

// [Log] really arg len – 5

// [Log] func arg len – 2

2.大家得以借用arguments.callee来让匿名函数达成递归:

var sum = function(n) {  

if(n == 1) {  

return 1;  

}else {  

return n + arguments.callee(n-1);  

 }  

}  

console.log(“sum =”, sum(5)); 

发表评论

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

网站地图xml地图