金沙棋牌官方平台

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

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

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-10-06 13:25

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语言。

类数组对象

所谓的类数组对象:

持有五个 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的规格。

读写

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>

长度

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;

遍历

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

因而究竟依旧类数组呐……

依傍完毕第三步

效仿代码已经完成 十分之九,还会有四个小点要静心:

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

变量赋值
  • ES5
var a = 1;  //window.a = 1;  全局变量
function(){var a = 1;} //只能在函数体内访问到变量a
  • ES6新添结构赋值
let a = 1; //window.a ===undefined;
{
let a,b,c;
[a,b,c=3] = [1,2];   // let a = 1; let b = 2; let b =3;
}
{
let a,b,c;
[a,,b,,c] = [1,2,3,4,5,6,7,8,9,10];
console.log(a,b,c); //1,3,5
}
{
let o = {a:1,b:2};
let {a,b} = o;
console.log(a,b);//1,2
}
{
let o = {a:1,b:2};
let {a=2,b} = o;
console.log(a,b);//1,2
}
{
let metaData = {
 number:'1',
 info:[{
name:'chen'
}]
};
let {number:Num,info:[{name:name}]} = metaData;
console.log(Num,name);   // Num:'1',name:'chen'
}
{
    function test(){
         return [1,2,3,4,5,6,7]
     }
  let a;
[...a] = test(); // let a = [1,2,3,4,5,6,7];
}
{
let a = 1; let b = 2;
[a,b] = [b,a];
console.log(a,b)  //变量交换
}
{
let a,b,c;
[a,b,...c] = [1,2,3,4,5,6,7];  // let a = 1;let b = 2; let c = [4,5,6,7];
}
{
let a,b;
({a,b} ={a:1,b:2});
console.log(a,b); // 1,2;
}

调用数组方法

只要类数组正是任性的想用数组的办法怎么办吧?

既然如此不可能直接调用,大家能够用 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"]

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 再删除它不就好了~
之所以大家模拟的步子能够分成:
将函数设为对象的品质
实施该函数
去除该函数

JavaScript 变量注解升高
  • ES5
console.log(a); //undefind
var a = 1;
//等同如下
var a;
console.log(a);  //undefind
a = 1;
  • ES6:let证明变量不升官
console.log(a); // ReferenceError: a is not defined
let a = 1;

类数组转对象

在上面包车型地铁事例中曾经涉及了一种类数组转数组的办法,再补偿多少个:

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()等)也回到类数组对象。

重视参谋

新浪难题 不能够使用call、apply、bind,如何用 js 落成 call 只怕 apply 的作用?

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

标识符
  • 概念:识别具体对象的四个称谓(大小写敏感),如变量名,函数名。
  • 规则:
    • 第2个字符,可是大肆Unicode字母,以及澳元符号($),和下划线(_)。
    • 第一个字符以及背后的字符,除了Unicode,欧元符号以及下划线,还足以是数字0-9。
  • 保留字和主要字无法看做标志符(如:var 、class、false、true)。

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

打印结果如下:

金沙棋牌官方平台 1

我们得以看看除了类数组的索引属性和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 深远之参数按值传递

    1 赞 收藏 评论

金沙棋牌官方平台 2

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

注释
  • 单行:/那是注释/。
  • 多行:/*那是注释*/。

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

恰好能够打印 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

区块(块级作用域)
  • ES5:官样文章块级效能域
{
var a = 1;
}
console.log(a); // 1
  • ES6:使用let、const表明变量或常量(存在块级成效域)
{
let a = 1; const b =1;
}
console.log(a); // ReferenceError: a is not defined
console.log(b); // ReferenceError: a is not defined
{
let a = 1;
let a = 2;
console.log(a) //"SyntaxError: Identifier 'a' has already been declared(同一作用域重复声明一个变量报错)。
}
{
var a = 1;
var a = 2;
console.log(a);//2 var 重复声明同一变量取最后一次声明的赋值。
}

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 对象的多少个注意要点:

在乎:传入的参数并不明确,这可咋做?
不急,大家能够从 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]

基准语句
  • if语句
if(true){
console.log('我被执行了')
}else{
console.log('我永远不会被执行')
}
  • switch语句
var f = 'apple';
if(f ==='banana'){
console.log('banana')
}else if(f==='apple'){
console.log('apple')
}
//多个if(){}else if{}嵌套时使用switch语句
switch(f){
case 'banana' : console.log('banana');
break;
case 'apple':console.log('apple');
break;
default: console.log('default');
}
  • 伊利运算符

    - expression ? do(true): do(false);

 let a = false;
 let b = a ? 1:2;
 console.log(b) // 2;
}```

- while循环语句
 - 

{
let i = 0;
while(i<10){
console.log(i); //0~9
i++;
}
}
{
let i = 11;
do{
console.log(i);
i--;
}while(i<10);
}

- for循环语句

for(let i=0;i<100;i++){
console.log(i);//0~99
}

- break和continue关键字

{
for(let i=0;i<10;i++){

if(i>=5){break;}   
 console.log(i); //0~4

}
}
{
for(let i=0; i<10;i++){
if(i<=5){continue;}
console.log(i);// 6~9
}
}

##### 数据类型
- 数值(number)
- 字符串(string)
- 布尔值(boolean) 
 - 5个假值(null,undefined,0,'',NaN)
- undefined
- null
- 对象(object)
  - 数组(Array)  是一种对象
  - 函数(Function) 是一种对象
  - 普通对象  

##### 类型转换

{
let number = 1;
let string = number+'';
console.log(typeof string,string) //string,"1"
}
{
let number = 1;
let bool =!number;
console.log(typeof bool,bool) //boolean,false
}
{
let string = '123';
//let number = string -0;
let number = +string;
console.log(typeof number,number) //number,123
}
{
let string = 'hello';
let number = string - 0;
console.log(typeof number,number) //NaN;
}
{
let bool = true;
let number = bool -0;
//let number = !bool -0; number, 0
console.log(typeof number,number) //number,1
}

- ##### 字符串方法以及遍历
  - ES5

//遍历
{
let string = "hello";
for(let i = 0;i<string.length;i++){

console.log(string[i])

}
}
//method
{
let str = 'hello';
let newStr = str.substring(1,2); // [start,end)
console.log(str); // 'hello'
console.log(newStr) // 'e'
}
{
let str = 'world';
let newStr = str.substr(1,2); //start, deleteCount
console.log(str); // 'world'
console.log(newStr) // 'or'
}

- ES6

{
let string = "world";
for(let i of string){
console.log(i)
}
}

 ##### 声明对象以及读写属性、遍历对象
- Obejct是一种无序的集合
 - ES5

{
let o = {
name:'小花',
age:18,
skill: function(){console.log('say')}
};
/*let o = new Object({
name:'小花'
}) */
console.log(o.name); //"小花"
o.name = '小草';
console.log(o['name']);//"小草"
console.log('name' in o); //true
delete o.name; //o = {};
}
{
let o = {
name:'小草',
age:18
}
for(let i in o){
console.log(i); //name,age
console.log(o[i]); //小草,18
}
}

 - ES6

{
let name = 'xiaohua',age = 16;
let o = {
name,
age,
skill(){
console.log('say')
}
}
console.log(o.skill())
}
{
let a = 'b';
let es5_obj = {
a:'c',
b:'c'
}
let es6_obj ={
[a]:'c' //key能够用变量
}
console.log(es5_obj,es6_obj);
}

##### 声明数组、遍历数组
- Array是一种有序的集合

- 数组的一些方法
  - ES5

{
let array = [1,2,3,[4,5,6],{5:"6",6:"7",7:"8"}]; //注解数组
console.log(array);
console.log(array.length);//5;
for(let i = 0; i<array.length;i++){
console.log(i,"-",array[i]);
}
array.push(9,10,11,[12,13,14],{name:"array"});
console.log(array);
array.pop();
console.log(array.length);
}
{
let arr = [2,3,1,4,5];
arr.sort();
console.log(arr);//[1,2,3,4,5]
arr.sort(function(a,b){return a<b});
console.log(arr);//[5,4,3,2,1]
}
{
let arr = [1,2,3,4,5];
let deleteArr = arr.splice(0,2,0,1,2);//array.splice(start, deleteCount, item1, item2, ...)
console.log(arr);
console.log(deleteArr);
}
{
let arr = [1,2,3,4];
let arrStr = arr.join('--');
console.log(arr);
console.log(arrStr);
let newArrStr = arrStr.split('--');
console.log(newArrStr);
}

  - ES6

{ //将伪数组转变到数组
function arg(){
argArray = Array.from(arguments,(item)=> item2); //Array.from(arrayLike[, mapFn[, thisArg]])
console.log(argArray)
}
/

argArray = Array.from(arguments);
argArray.forEach(function(item){console.log(item)})
*/
arg(1,2,3,4,5)
}
{ //填充数组
let array = [1,2,3,4,5]; //arr.fill(value) arr.fill(value, start) arr.fill(value, start, end)
newArray = array.fill(0);
console.log(newArray);
console.log(array);
console.log(array.fill(9,0,3));
console.log(array);
}
{ //遍历数组
let array = [1,2,3,4,5];
for(let i of array){
console.log(i) //1,2,3,4,5
}
for(let i of array.keys()){
console.log(i)//0,1,2,3,4
}
for(let [i,v] of array.entries()){
console.log(i,v)
}
console.log(array.find((item)=>item>3)); //查找满意条件,只回去第三个
console.log(array.findIndex(item=>item>3));
{
let array = [1,2,3,4,5];
console.log(array.includes(1,0))//arr.includes(searchElement, fromIndex) //是还是不是含有
}
}

##### 声明函数,函数提升,arguments及...rest,length属性,闭包,同步V.S.异步
 - ES5

// var say = function(){}; 只会晋级var say
function say(x){ //提高整个函数
console.log(x);
console.log(arguments) //将传入全部实参生成三个伪数组,其实是三个key为平稳下标的靶子
return x //使函数具备再次回到值
}
say('hello'); //传入实参
console.log(say.length);//行参个数
var c =say('hello'); //重临值赋予变量c
console.log(c);
{ //马上推行函数 幸免全局污染
!function(){
var a = 1;
console.log(a)
}();
!function(){
var a = 2;
console.log(a)
}();
}
{ //闭包
function f1(){
var a = 1;
function f2(){
a++;
console.log(a)
}
return f2;
}

let result = f1();
result();
}
{//同步
console.log(1);
console.log(2);
console.log(3);
}
{//异步
console.log(1);
setTimeout(function(){
console.log(2);
},3000)
console.log(3);
}

 - ES6

{ //ES6存在块及功用域,不要求使用无名氏函数来防护全局污染
let a =1 ;
console.log(a);
}
{
let a = 2;
console.log(a);
}
{
function say(x,y = 'world'){ //行参私下认可值
console.log(x,y);
}
say('hello');
}
{
let say = (...arg)=>{
console.log(arg);
for(let i of arg){
console.log(i);
}
console.log(typeof arg.push) //那是四个真数组,和arguments分裂
}
say('hello','world');
}
{
let x = 'china';
let say = (x,y = x)=>{
console.log(x,y);
}
say('hello');//"hello hello"
}
{
let x = 'china';
let say = (z,y = x)=>{ //变量作用域,和上一个例子相比较
console.log(z,y);
}
say('hello');//"hello china"
}
{
let say = (x)=> x ;//此处若是加{}就不会有重回值
/*
var say = function(x){
return x
}
*/
let result = say(100);
console.log(result)
}
{ //函数作为再次回到值,函数作为参数的例子
let qux= ()=> (callback)=> callback();
let result = qux();
console.log(result);
result(()=>{console.log("执行了")})
}

类、原型、继承(面向对象)
  - ES5

{
function Person(name,age,gender){
this.name = name;
this.age =age;
this.gender = gender;
}
Person.prototype.born = function(){
console.log('born')
}
function Man(){
Person.apply(this,arguments)
this.sex = 'male'
}
let empty = function(){};
empty.prototype = Person.prototype;
Man.prototype = new empty();
console.log(Man.prototype.constructor = Man);
var man1 = new Man('张三',18,'male');
console.log(man1)
}
{
var name,age,gender;
var Person = {
name:name,
age:age,
gender:gender,
born:function(){console.log('born')}
}
var Man = Object.create(Person);
Man.sex = 'male';
console.log(Man)
}

  - ES6 

{//ES6 类
class Person{
constructor(name='张三',age= 18,gender='male'){
this.name = name;
this.age =age;
this.gender = gender;
};
born(){
console.log('born')
};
die(){
console.log('die')
}
}
console.log(new Person)
class Man extends Person{//类的继续
constructor(){
super();
this.sex = 'Man'
}
}
let man1 = new Man()
console.log(man1)
console.log(man1.born())
}

##### 标准库
 - Array
 - String
 - Number
 - Function
 - Boolean
 - Math(console.dir(Math)  )
  - Math.PI;              //3.141592653589793
  - Math.SQRT2;      //1.4142135623730951
  -Math.pow();
  -Math.sqrt(); 
  - Math.random()*50+50 ;// 50~100之间的伪随机数
 - Date
  - new Date() 
    - 
       ```
{
let date = new Date();
  console.log(date);//Sat Jun 03 2017 01:27:41 GMT+0800 (CST)
  console.log(date.getFullYear())  //2017
  console.log(date.getMonth()) // 5   0~11个月
  console.log(date.getDate())  //3    
  console.log(date.getDay())  //6 星期日为0,星期一为1。
  console.log(date.getHours());
  console.log(date.getMinutes())
  console.log(date.getSeconds())
}
  • toLocaleString()

  • Promise

{
  function breakfast(callback){
     console.log('吃早饭');
     callback&&callback();
  }
  function lunch(){
     console.log('吃午饭');
  }
  console.log(breakfast(lunch))
}
{
  let breakfast = function(){
    console.log('吃早饭');
    return new Promise(function(resolve,reject){
      resolve();
    })
  } 
  let lunch = function(){
    console.log('吃午饭');
    return new Promise(function(resolve,reject){
     resolve();
    })
  }
  let dinner = function(){
    console.log('吃晚饭')
  }
 breakfast().then(lunch).then(dinner)
}

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 是不会共享的。

不定长的参数难题消除了,大家跟着要把那些参数数组放到要实行的函数的参数里面去。
// 将数组里的成分作为多少个参数放进函数的形参里context.fn(args.join(','))// (O_o)??// 那一个情势自然是十一分的呐!!!

传递参数

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

// 使用 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)

莫不有人想到用 ES6 的方法,可是 call 是 ES3 的法子,我们为了模仿实现八个ES3 的办法,要用到ES6的办法,好像……,嗯,也能够啊。可是我们本次用 eval 方法拼成三个函数,类似于如此:
eval('context.fn(' + args +')')

强大的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);

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

应用

arguments的应用其实过多,在下个密密麻麻,也正是 JavaScript 专项论题种类中,我们会在 jQuery 的 extend 完成、函数柯里化、递归等气象看见arguments 的人影。那篇小说就不具体进展了。

只要要总括那个现象的话,一时能体会领会的牢笼:

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

接待留言回复。

(๑•̀ㅂ•́)و✧
依傍完结第三步
效仿代码已经实现 五分之四,还会有八个小点要在意:
1.this 参数能够传 null,当为 null 的时候,视为指向 window
举个例证:
var value = 1;function bar() { console.log(this.value);}bar.call(null); // 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的模仿完毕
  11. JavaScript 深刻之bind的模拟完结
  12. JavaScript 长远之new的模仿达成

    1 赞 2 收藏 评论

金沙棋牌官方平台 3

固然那些事例本人不应用 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// }

可是都很好化解,让大家直接看第三版也便是最终一版的代码:
// 第三版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的模拟实现,深入之类数组对象

关键词: