金沙棋牌官方平台

当前位置:金沙棋牌 > 金沙棋牌官方平台 > 深入之类数组对象与,深入之call和apply的模拟实

深入之类数组对象与,深入之call和apply的模拟实

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-10-04 14:31

JavaScript 深切之bind的一成不改变达成

2017/05/26 · JavaScript · bind

原稿出处: 冴羽   

JavaScript 深刻之call和apply的效仿完毕

2017/05/25 · 金沙棋牌官方平台,JavaScript · apply, call

原来的小讲出处: 冴羽   

经过call和apply的效仿达成,带你爆料call和apply更动this的本来面目

JavaScript 深远之类数组对象与 arguments

2017/05/27 · JavaScript · arguments

原稿出处: 冴羽   

bind

一句话介绍 bind:

bind() 方法会创立三个新函数。当以此新函数被调用时,bind() 的首先个参数将作为它运转时的 this,之后的一连串参数将会在传递的实参前传出作为它的参数。(来自于 MDN )

由此大家得以率先得出 bind 函数的多个特点:

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

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 值和若干个钦命的参数值的前提下调用某些函数或艺术。

类数组对象

所谓的类数组对象:

抱有叁个 length 属性和若干索引属性的目的

比方:

var array = ['name', 'age', 'sex']; var arrayLike = { 0: 'name', 1: 'age', 2: 'sex', length: 3 }

1
2
3
4
5
6
7
8
var array = ['name', 'age', 'sex'];
 
var arrayLike = {
    0: 'name',
    1: 'age',
    2: 'sex',
    length: 3
}

即使如此,为啥叫做类数组对象呢?

那让咱们从读写、获取长度、遍历四个地点看看那四个对象。

回到函数的模拟达成

从第三个特点开首,大家举例:

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

依傍完毕率先步

那么大家该怎么模拟实现那多个功效啊?

试想当调用 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 对象自己增添了一个属性,那可不行呀!

而是也不用顾虑,我们用 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 函数:

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

读写

console.log(array[0]); // name console.log(arrayLike[0]); // name array[0] = 'new name'; arrayLike[0] = 'new name';

1
2
3
4
5
console.log(array[0]); // name
console.log(arrayLike[0]); // name
 
array[0] = 'new name';
arrayLike[0] = 'new name';

传参的模仿完成

接下去看第二点,能够流传参数。这几个就有一点点令人费解了,作者在 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.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)); } }

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

模仿完成第二步

最一开首也讲了,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 对象中取值,抽取第一个到最后多少个参数,然后放到三个数组里。

比如那样:

// 以上个例子为例,此时的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)?? // 那几个方式断定是不行的呀!!!

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 函数实施了

长度

console.log(array.length); // 3 console.log(arrayLike.length); // 3

1
2
console.log(array.length); // 3
console.log(arrayLike.length); // 3

构造函数效果的模拟达成

形成了这两点,最难的部分到啦!因为 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长远之从原型到原型链》。

依傍达成第三步

模仿代码已经完结 九成,还会有七个小点要介意:

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

遍历

for(var i = 0, len = array.length; i len; i++) { …… } for(var i = 0, len = arrayLike.length; i len; i++) { …… }

1
2
3
4
5
6
for(var i = 0, len = array.length; i  len; i++) {
   ……
}
for(var i = 0, len = arrayLike.length; i  len; i++) {
    ……
}

是否很像?

那类数组对象足以应用数组的法门吗?举例:

arrayLike.push('4');

1
arrayLike.push('4');

而是上述代码会报错: arrayLike.push is not a function

为此毕竟依然类数组呐……

构造函数效果的优化完成

可是在那一个写法中,我们平素将 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

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 对象自作者增多了叁个性能,这可特别啊!
然则也不用顾虑,大家用 delete 再删除它不就好了~
之所以大家模拟的步调可以分成:
将函数设为对象的质量
执行该函数
去除该函数

调用数组方法

一旦类数组正是随便的想用数组的措施如何是好呢?

既是无法直接调用,大家能够用 Function.call 间接调用:

var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 } Array.prototype.join.call(arrayLike, '&'); // name&age&sex Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] // slice能够成功类数组转数组 Array.prototype.map.call(arrayLike, function(item){ return item.toUpperCase(); }); // ["NAME", "AGE", "SEX"]

1
2
3
4
5
6
7
8
9
10
11
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
 
Array.prototype.join.call(arrayLike, '&'); // name&age&sex
 
Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"]
// slice可以做到类数组转数组
 
Array.prototype.map.call(arrayLike, function(item){
    return item.toUpperCase();
});
// ["NAME", "AGE", "SEX"]

五个没不日常

接下去管理些小意思:

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))

多了一个有关 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啦。

重大参谋

天涯论坛难题 不能够应用call、apply、bind,如何用 js 达成 call 或许 apply 的效果?

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

类数组转对象

在上边的事例中早已涉及了一种类数组转数组的不二等秘书诀,再补偿七个:

var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 } // 1. slice Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"] // 2. splice Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"] // 3. ES6 Array.from Array.from(arrayLike); // ["name", "age", "sex"] // 4. apply Array.prototype.concat.apply([], arrayLike)

1
2
3
4
5
6
7
8
9
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
// 1. slice
Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"]
// 2. splice
Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"]
// 3. ES6 Array.from
Array.from(arrayLike); // ["name", "age", "sex"]
// 4. apply
Array.prototype.concat.apply([], arrayLike)

这正是说为何会讲到类数组对象呢?以及类数组有哪些应用吗?

要说起类数组对象,Arguments 对象就是二个类数组对象。在顾客端 JavaScript 中,一些 DOM 方法(document.getElementsByTagName()等)也回到类数组对象。

最终代码

因此最末尾的代码便是:

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

深入体系

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: 1};function bar() { console.log(this.value);}bar.call2(foo); // 1

Arguments对象

接下去入眼讲讲 Arguments 对象。

Arguments 对象只定义在函数体中,包涵了函数的参数和其余品质。在函数体中,arguments 指代该函数的 Arguments 对象。

比如:

function foo(name, age, sex) { console.log(arguments); } foo('name', 'age', 'sex')

1
2
3
4
5
function foo(name, age, sex) {
    console.log(arguments);
}
 
foo('name', 'age', 'sex')

打字与印刷结果如下:

金沙棋牌官方平台 2

小编们能够看到除了类数组的索引属性和length属性之外,还会有三个callee属性,接下去我们二个多少个介绍。

深远连串

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

金沙棋牌官方平台 3

凑巧能够打字与印刷 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

length属性

Arguments对象的length属性,表示实参的长短,举个例证:

function foo(b, c, d){ console.log("实参的尺寸为:" + arguments.length) } console.log("形参的长短为:" + foo.length) foo(1) // 形参的长度为:3 // 实参的长度为:1

1
2
3
4
5
6
7
8
9
10
function foo(b, c, d){
    console.log("实参的长度为:" + arguments.length)
}
 
console.log("形参的长度为:" + foo.length)
 
foo(1)
 
// 形参的长度为:3
// 实参的长度为:1

在乎:传入的参数并不鲜明,那可怎么办?
不急,大家得以从 Arguments 对象中取值,抽出第三个到最后一个参数,然后嵌入三个数组里。
比方那样:
// 以上个例子为例,此时的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]

callee属性

Arguments 对象的 callee 属性,通过它能够调用函数自个儿。

讲个闭包美貌面试题使用 callee 的化解办法:

var data = []; for (var i = 0; i 3; i++) { (data[i] = function () { console.log(arguments.callee.i) }).i = i; } data[0](); data[1](); data[2](); // 0 // 1 // 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var data = [];
 
for (var i = 0; i  3; i++) {
    (data[i] = function () {
       console.log(arguments.callee.i)
    }).i = i;
}
 
data[0]();
data[1]();
data[2]();
 
// 0
// 1
// 2

接下去讲讲 arguments 对象的多少个注意要点:

不定长的参数难题一蹴即至了,大家跟着要把那几个参数数组放到要实施的函数的参数里面去。
// 将数组里的成分作为四个参数放进函数的形参里context.fn(args.join(','))// (O_o)??// 这一个点子料定是不行的呀!!!

arguments 和呼应参数的绑定

function foo(name, age, sex, hobbit) { console.log(name, arguments[0]); // name name // 改变形参 name = 'new name'; console.log(name, arguments[0]); // new name new name // 改变arguments arguments[1] = 'new age'; console.log(age, arguments[1]); // new age new age // 测量检验未传入的是或不是会绑定 console.log(sex); // undefined sex = 'new sex'; console.log(sex, arguments[2]); // new sex undefined arguments[3] = 'new hobbit'; console.log(hobbit, arguments[3]); // undefined new hobbit } foo('name', 'age')

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
function foo(name, age, sex, hobbit) {
 
    console.log(name, arguments[0]); // name name
 
    // 改变形参
    name = 'new name';
 
    console.log(name, arguments[0]); // new name new name
 
    // 改变arguments
    arguments[1] = 'new age';
 
    console.log(age, arguments[1]); // new age new age
 
    // 测试未传入的是否会绑定
    console.log(sex); // undefined
 
    sex = 'new sex';
 
    console.log(sex, arguments[2]); // new sex undefined
 
    arguments[3] = 'new hobbit';
 
    console.log(hobbit, arguments[3]); // undefined new hobbit
 
}
 
foo('name', 'age')

盛传的参数,实参和 arguments 的值会分享,当未有传到时,实参加 arguments 值不会分享

除开,以上是在非严刻格局下,如若是在严酷形式下,实参和 arguments 是不会分享的。

也有人想到用 ES6 的点子,不过 call 是 ES3 的点子,大家为了模仿达成一个ES3 的主意,要用到ES6的秘技,好像……,嗯,也足以啊。然而大家此次用 eval 方法拼成二个函数,类似于如此:
eval('context.fn(' + args +')')

传送参数

将参数从三个函数字传送递到另二个函数

// 使用 apply 将 foo 的参数字传送递给 bar function foo() { bar.apply(this, arguments); } function bar(a, b, c) { console.log(a, b, c); } foo(1, 2, 3)

1
2
3
4
5
6
7
8
9
// 使用 apply 将 foo 的参数传递给 bar
function foo() {
    bar.apply(this, arguments);
}
function bar(a, b, c) {
   console.log(a, b, c);
}
 
foo(1, 2, 3)

此间 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

强大的ES6

接纳ES6的 … 运算符,大家得以轻便转成数组。

function func(...arguments) { console.log(arguments); // [1, 2, 3] } func(1, 2, 3);

1
2
3
4
5
function func(...arguments) {
    console.log(arguments); // [1, 2, 3]
}
 
func(1, 2, 3);

(๑•̀ㅂ•́)و✧
效仿达成第三步
依傍代码已经变成 十分九,还大概有两个小点要潜心:
1.this 参数可以传 null,当为 null 的时候,视为指向 window
举例:
var value = 1;function bar() { console.log(this.value);}bar.call(null); // 1

应用

arguments的应用其实过多,在下个密密麻麻,也正是 JavaScript 专题体系中,大家会在 jQuery 的 extend 完成、函数柯里化、递归等情景见到arguments 的身材。那篇小说就不具体进展了。

假若要总括那个场景的话,权且能体会掌握的归纳:

  1. 参数不定长
  2. 函数柯里化
  3. 递归调用
  4. 函数重载

招待留言回复。

即便那几个例子本人不应用 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// }

深切类别

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的模仿实现
  11. JavaScript 深切之bind的模拟达成
  12. JavaScript 深切之new的模仿完毕

    1 赞 2 收藏 评论

金沙棋牌官方平台 4

唯独都很好消除,让大家一贯看第三版约等于最终一版的代码:
// 第三版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// }

到此,大家做到了 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深切体系目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript深远体系预计写十五篇左右,意在帮大家捋顺JavaScript底层知识,入眼讲授如原型、成效域、实行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、承接等难点概念。
借使有错误只怕不严厉的地方,请必得给予指正,拾贰分感激。如若喜欢大概具备启发,招待star,对小编也是一种鞭挞。

本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:深入之类数组对象与,深入之call和apply的模拟实

关键词:

上一篇:没有了

下一篇:没有了