xml地图|网站地图|网站标签 [设为首页] [加入收藏]

JavaScript深入之call和apply的模拟实现,深入之cal

来源:http://www.ccidsi.com 作者:集成经验 人气:112 发布时间:2019-05-02
摘要:效仿实现第3步 依傍代码已经产生 八成,还有七个小点要留意: 1.this 参数能够传 null,当为 null 的时候,视为指向 window 比方: var value = 1; function bar() { console.log(this.value); }bar.call(null

效仿实现第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,结果依然照旧同样。

二.函数是足以有再次来到值的!

举个例证:

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

唯独都很好消除,让我们直接看第1版也正是最终1版的代码:

// 第三版 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', 1八)); // 一 // Object { // value: 1, // 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

莫不有人想到用 ES陆 的不二等秘书技,可是 call 是 ES三 的不二等秘书诀,大家为了模仿达成一个ES三 的形式,要用到ES陆的方法,好像……,嗯,也能够啊。可是大家此次用 eval 方法拼成三个函数,类似于那般:
eval('context.fn(' args ')')

深深种类

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 长远之参数按值传递
  10. JavaScript 长远之call和apply的因循古板落成

    1 赞 收藏 评论

图片 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 深切之参数按值传递

    1 赞 收藏 评论

图片 2

到此,大家完毕了 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;}

JavaScript 深切之bind的模仿完结

2017/05/26 · JavaScript · bind

原来的文章出处: 冴羽   

call

一句话介绍 call:

call() 方法在采取1个钦点的 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 函数施行了

可是都很好化解,让大家一贯看第一版约等于终极一版的代码:
// 第三版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); // 二console.log(bar.call二(obj, 'kevin', 1八));// 1// Object {// value: 一,// name: 'kevin',// age: 18// }

传参的一成不变落成

接下去看第二点,能够流传参数。这几个就有点令人费解了,笔者在 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,在奉行回来的函数的时候,再传另二个参数 age!

那可如何是好?不急,我们用 arguments 举行管理:

// 第三版 Function.prototype.bind贰 = function (context) { var self = this; // 获取bind2函数从第二个参数到终极七个参数 var args = Array.prototype.slice.call(arguments, 壹); 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));
    }
 
}

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

那边 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// 一

构造函数效果的优化完结

而是在这几个写法中,大家直接将 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

画虎类犬完成第壹步

最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

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个到最终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