JavaScript深切之call和apply的模拟已毕,深入之类数组对象与

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’]JavaScript深切之call和apply的模拟已毕,深入之类数组对象与。; 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深入之从原型到原型链》。

东施效颦完结第三步

效仿代码已经成功 80%,还有四个小点要注意:

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

(๑•̀ㅂ•́)و✧
如法泡制达成第三步
效仿代码已经成功 80%,还有八个小点要留心:
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,对作者也是一种鞭策。

网站地图xml地图