举个例子必威:,带你揭开call和apply改变this的真

JavaScript 深切之类数组对象与 arguments

2017/05/27 · JavaScript · arguments

原稿出处: 冴羽   

JavaScript 深远之call和apply的模拟达成

2017/05/25 · JavaScript · apply, call

初稿出处: 冴羽   

由此call和apply的一成不改变完结,带您揭秘call和apply退换this的真相

JavaScript之父:Brendan Eich 。

-基本语法:借鉴了C语言和Java语言。
-数据结构:借鉴了Java,包含将值分成原始值和对象两大类。

  • 函数的用法:借鉴了Scheme和Awk语言,将函数当成第一等公民,引进闭包。
  • 原型承袭模型:借鉴了Self语言。
  • 正则表明式:借鉴了Perl语言。
  • 字符串和数组管理:借鉴了Python语言。

JavaScript 深入之bind的照猫画虎实现

2017/05/26 · JavaScript · bind

原稿出处: 冴羽   

类数组对象

所谓的类数组对象:

持有二个 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
}

尽管如此,为啥叫做类数组对象啊?

这让我们从读写、获取长度、遍历三个方面看看那四个目的。

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 值和几何个钦点的参数值的前提下调用有个别函数或措施。

JavaScript与ECMAScript的关系?
  • ECMAScript规定了浏览器脚本语言的行业内部。
  • ECMAScript是JavaScript的规格。

bind

一句话介绍 bind:

bind() 方法会创设贰个新函数。当这几个新函数被调用时,bind() 的率先个参数将作为它运营时的 this,之后的一种类参数将会在传递的实参前传出作为它的参数。(来自于 MDN )

通过大家得以率先得出 bind 函数的三个特色:

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

读写

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

效仿实现率先步

那正是说大家该怎么模拟实现那多个职能呢?

试想当调用 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

哪些在浏览器中运转JavaScript?
  • <script> console.log('运行JS') </script>
  • <script src='./*'> </script>

回来函数的模仿完成

从第一个特征开首,大家比如:

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

长度

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

1
2
console.log(array.length); // 3
console.log(arrayLike.length); // 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 对象中取值,抽取第二个到终极三个参数,然后放到贰个数组里。

比方说那样:

// 以上个例子为例,此时的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 函数实施了

JavaScript 注脚变量
  • var a;
  • let a;

传参的模拟完毕

接下去看第二点,能够流传参数。那些就有一点点令人费解了,笔者在 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和apply改变this的真

相关阅读