JavaScript深远之call和apply的模拟完毕,深远之call和apply的依样葫芦已毕

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() 方法会成立一个新函数。当以此新函数被调用时,bind()
的第四个参数将作为它运行时的
this,之后的一种类参数将会在传递的实参前流传作为它的参数。(来自于 MDN
)

因此我们得以率先得出 bind 函数的八个性状:

  1. 归来一个函数
  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 对象自我添加了一个性能,那可不行呀!

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

依据那一个思路,大家得以品味着去写第一版的 call2JavaScript深远之call和apply的模拟完毕,深远之call和apply的依样葫芦已毕。 函数:

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

回来函数的效仿完结

从第四个性状开始,大家举个例子:

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

仿照达成第二步

最一开首也讲了,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 函数执行了

传参的模拟达成

接下去看第二点,可以流传参数。那么些就有点令人费解了,我在 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));
    }
 
}

    var user =”追梦子”;

一成不变完成第三步

效仿代码已经成功 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

构造函数效果的模仿完毕

成就了那两点,最难的有的到啦!因为 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 对象自我添加了一个性能,那可不行呀!
可是也不用担心,我们用 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

三个小题目

接下去处理些不是问题:

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啦。

}

深刻序列

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

最后代码

故而最末尾的代码就是:

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 哎!是或不是很喜出望外!(~ ̄▽ ̄)~
宪章完结第二步
最一初步也讲了,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、继承等困难概念。

假若有错误或者不小心的地方,请务必给予指正,相当感谢。假诺喜欢仍然有所启发,欢迎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
对象中取值,取出第三个到最后一个参数,然后放到一个数组里。
譬如那样:
// 以上个例子为例,此时的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 的方法,大家为了模仿完结一个
ES3 的法子,要用到ES6的办法,好像……,嗯,也得以啊。但是大家本次用 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: 1};function
bar(name, age) { console.log(name) console.log(age)
console.log(this.value);}bar.call2(foo, ‘kevin’, 18); // kevin// 18// 1

            }

(๑•̀ㅂ•́)و✧
仿照完结第三步
宪章代码已经形成 80%,还有四个小点要留心:
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、继承等困难概念。
假如有不当或者不严酷的地点,请务必给予指正,极度感谢。假若喜欢依旧具有启发,欢迎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
 由于再次来到了一个对象所以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

1、大家得以借用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地图