Array. prototype.slice()
slice()方法从数组中由开始到结束(不含结束)选择一部分拷贝到一个新的数组并返回。原数组不会被修改。
语法: | arr.slice() |
| arr.slice(begin) |
| arr.slice(begin,end) |
参数: | begin:可选,开始的索引位置(0开始),省略默认为0,可以为负数,负数表示从原数组倒数计算。slice(-2)表示数组中倒数第二个元素到最后一个元素(含最后元素)。 |
| end:可选。索引结束位置(从0开始),不含end位置,省略默认提取到原数组末尾。slice(1,4)表示拷贝第1,2,3索引位置数组。 |
Array. prototype. splice()
splice()方法通过删除现有元素和/或添加新元素来更改原数组的内容。原数组被修改,返回删除掉的元素组成的数组。
语法: | array.splice(start) |
| array.splice(start,deleteCount) |
| array.splice(start, deleteCount, items, items2,…) |
参数: | start:指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从1计数) |
| deleteCount:可选,整数,表示要移除的数组元素的个数。如果 deleteCount 是 0,则不移除元素。这种情况下,至少应添加一个新元素。如果 deleteCount 大于start 之后的元素的总数,则从start 后面的元素都将被删除(含第 start 位)。 如果deleteCount被省略,则其相当于(arr.length - start)。 |
| item1, item2, …可选,要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。 |
返回值 | 由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。 |
Array. prototype. join()
join() 方法将数组(或一个类数组对象)的所有元素连接到一个字符串中。不会改变原数组
语法:
str = arr.join() //默认“,”
str = arr.join("") //空字符串,元素间没有任何字符
str = arr.join(separator)
参数:
separator:指定一个字符串来分隔数组的每个元素。如果需要(separator),将分隔符转换为字符串。
返回值:
一个所有数组元素连接的字符串。如果 arr.length 为0,则返回空字符串。
join() 方法将数组(或一个类数组对象)的所有元素连接到一个字符串中。不会改变原数组
语法: | str = arr.join() //默认“,” |
| str = arr.join("") //空字符串,元素间没有任何字符 |
| str = arr.join(separator) |
参数: | separator:指定一个字符串来分隔数组的每个元素。如果需要(separator),将分隔符转换为字符串。 |
返回值: | 一个所有数组元素连接的字符串。如果 arr.length 为0,则返回空字符串。 |
Array. prototype. fill()
fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。
语法: | arr.fill(value) |
| arr.fill(value, start) |
| arr.fill(value, start, end) //[start, end) ,一个半开半闭区间 |
参数: | value:用来填充数组元素的值。 start: 可选,起始索引,默认值为0。 end: 可选,终止索引,默认值为 this.length。 |
返回值: | 修改后的数组 |
[1, 2, 3].fill(4) // [4, 4, 4]
[1, 2, 3].fill(4, 1) // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2) // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1) // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2) // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN) // [1, 2, 3]
Array(3).fill(4); // [4, 4, 4]
[].fill.call({length: 3}, 4) // {0: 4, 1: 4, 2: 4, length: 3}
Array. prototype. reduce() & reduceRight()
化简操作函数,reduce() 方法对累加器和数组中的每个元素 (从左到右)应用一个函数,将其减少为单个值。
语法:
arr.reduce(callback, [initialValue])
参数:
callback:执行数组中每个值的函数,包含下面四个参数
accumulator:上一次调用回调返回的值,或者是提供的初始值(initialValue)
currentValue:数组中正在处理的元素
currentIndex:数据中正在处理的元素索引,如果提供了 initialValue,从0开始;否则从1开始
array:调用 reduce的数组
initialValue:可选项,其值用于第一次调用 callback的第一个参数。
返回值
函数累计处理的结果
示例:
将数组所有项相加
var sum = [0, 1, 2, 3].reduce(function(a, b) {
returna + b;
}, 0);
// sum is 6
或者用箭头函数编写
var total = [ 0, 1, 2, 3 ].reduce(
( acc, cur ) => acc + cur,
0
);
数组扁平化
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(a, b) {
returna.concat(b);
},
[]
);
// flattened is [0, 1,2, 3, 4, 5]
或者用箭头函数编写
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
( acc, cur ) =>acc.concat(cur),
[]
);
计算数组中各个值出现次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function(allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
returnallNames;
}, {});
// countedNames is {'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
兼容旧环境(Polyfill)
Array.prototype.reduce 被添加到 ECMA-262标准第5版;因此可能在某些实现环境中不被支持。可以将下面的代码插入到脚本开头来允许在那些未能原生支持 reduce 的实现环境中使用它。
// Production steps of ECMA-262,Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, 'reduce', {
value: function(callback/*, initialValue*/) {
if (this === null) {
thrownew TypeError( 'Array.prototype.reduce' +
'called on null or undefined' );
}
if (typeofcallback !== 'function') {
thrownew TypeError( callback +
' is not a function');
}
// 1. Let Obe ? ToObject(this value).
var o= Object(this);
// 2. Letlen be ? ToLength(? Get(O, "length")).
varlen = o.length>>> 0;
// Steps 3,4, 5, 6, 7
var k= 0;
varvalue;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while(k <len && !(k in o)) {
k++;
}
// 3. If lenis 0 and initialValue is not present,
// throw aTypeError exception.
if (k >= len) {
thrownew TypeError( 'Reduceof empty array ' +
'with no initial value' );
}
value = o[k++];
}
// 8.Repeat, while k < len
while(k <len) {
// a. Let Pk be ! ToString(k).
// b. Let kPresent be ? HasProperty(O, Pk).
// c. If kPresent is true, then
// i. Let kValue be ? Get(O, Pk).
// ii. Letaccumulator be ? Call(
// callbackfn, undefined,
// «accumulator, kValue, k, O »).
if (k in o) {
value = callback(value, o[k], k, o);
}
// d.Increase k by 1.
k++;
}
// 9. Returnaccumulator.
returnvalue;
}
});
}
Function.prototype.reduceRight
reduceRight() 方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
Array. prototype. map()
map()方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
语法:
arr.reduce(callback, [initialValue])
参数:
callback:生成新数组元素的,包含下面三个参数
currentValue:数组中正在处理的元素
index:数据中正在处理的元素索引
array:调用 map的数组
thisArg:可选项,执行callback函数时使用的this值。
返回值
函数累计处理的结果
示例:
// 使用三个参数
const numbers = [1, 2, 3, 4, 5];
let arr =numbers.map((currentValue, index, array) => {
console.log(`currentValue = \n`,currentValue);
console.log(`index = \n`, index);
console.log(`array= \n`, array);
returncurrentValue * 2;
}, numbers);
console.log(`arr \n`,arr);
let numbers = [1, 5, 10, 15];
let doubles = numbers.map((x) => {
returnx * 2;
});
// doublesis now [2, 10, 20, 30]
// numbers is still [1,5, 10, 15]
let numbers = [1, 4, 9];
let roots =numbers.map(Math.sqrt);
// roots isnow [1, 2, 3]
// numbers is still [1,4, 9]
Array. prototype. keys() & values()
keys() 方法返回一个新的Array迭代器,它包含数组中每个索引的键。
let arr = ["a", "b", "c"];
let iterator = arr.keys();
// undefined
console.log(iterator);
// Array Iterator {}
console.log(iterator.next());
// Object {value: 0,done: false}
console.log(iterator.next());
// Object {value: 1,done: false}
console.log(iterator.next());
// Object {value: 2,done: false}
console.log(iterator.next());
// Object {value:undefined, done: true}
values() 方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值。
PS: Chrome未实现
使用 for...of 循环进行迭代
let arr = ['w', 'y', 'k', 'o', 'p'];
let eArr = arr.values();
//您的浏览器必须支持 for..of 循环
//以及 let —— 将变量作用域限定在 for 循环中
for (let letter of eArr) {
console.log(letter);
}
另一种迭代方式
let arr = ['w', 'y', 'k', 'o', 'p'];
let eArr = arr.values();
console.log(eArr.next().value); // w
console.log(eArr.next().value); // y
console.log(eArr.next().value); // k
console.log(eArr.next().value); // o
console.log(eArr.next().value); // p
Function. prototype. bind()
bind()方法是创建一个新的函数,当被调用时,将this关键字设置为提供的值thisArg(即改变调用方法的上下文this),以及在调用新函数时,在已经存在的实参之前添加新加的参数列表[arg1,arg2]。
注意:该方法的是直接返回新的函数,执行还需要加括号(),而call和apply是直接执行。
语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数:
thisArg -当绑定函数被调用时,该参数会作为原函数运行时的 this指向。当使用new操作符调用绑定函数时,该参数无效
arg1, arg2, … -当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值:
返回由指定的this值和初始化参数改造的原函数拷贝。
描述:
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
用法举例
创建绑定函数
bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都用给定的 this值(上下文)。避免有些情况:将一个方法从对象中拿出来,然后再调用,this变成调用的对象或者是全局对象window,影响运行结果。
this.x = 9;
var module ={
x: 81,
getX: function() { returnthis.x; }
};
module.getX(); // 返回 81
varretrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
偏函数(Partial Functions)
bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
functionlist() {
return Array.prototype.slice.call(arguments);
}
varlist1 = list(1, 2, 3); // [1, 2, 3]
//Create a function with a preset leading argument
varleadingThirtysevenList = list.bind(undefined, 37);
varlist2 = leadingThirtysevenList(); // [37]
varlist3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
配合 setTimeout
在默认情况下,使用 window.setTimeout()时,this 关键字会指向 window(或全局)对象。当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例。
functionLateBloomer() {
this.petalCount = Math.ceil(Math.random() *12) + 1;
}
//Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom= function() {
window.setTimeout(this.declare.bind(this), 1000);
//this表示LateBloomer对象,不执行bind会默认内部this为window
};
LateBloomer.prototype.declare= function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
varflower = new LateBloomer();
flower.bloom(); // 一秒钟后, 调用'declare'方法
快捷调用
在你想要为一个需要特定的 this值的函数创建一个捷径(shortcut)的时候,bind()方法也很好用。
你可以用 Array.prototype.slice来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以创建这样一个捷径:
varslice = Array.prototype.slice;
//...
slice.apply(arguments);
用 bind()可以使这个过程变得简单。在下面这段代码里面,slice是Function.prototype的 call()方法的绑定函数,并且将 Array.prototype的 slice()方法作为 this的值。这意味着我们压根儿用不着上面那个 apply()调用了。
//same as "slice" in the previous example
varunboundSlice = Array.prototype.slice;
varslice = Function.prototype.call.bind(unboundSlice);
//...
slice(arguments);
Polyfill(兼容旧浏览器)
bind 函数在 ECMA-262第五版才被加入;它可能无法在所有浏览器上运行。你可以部份地在脚本开头加入以下代码,就能使它运作,让不支持的浏览器也能使用 bind() 功能。
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeofthis !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
thrownew TypeError("Function.prototype.bind - what is trying to be boundis not callable");
}
varaArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
returnfToBind.apply(this instanceof fNOP
? this
: oThis || this,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype= newfNOP();
returnfBound;
};
}
上述算法和实际的实现算法还有许多其他的不同(尽管可能还有其他不同之处,却没有那个必要去穷尽):
- 这部分实现依赖于Array.prototype.slice(), Array.prototype.concat(), Function.prototype.call()这些原生方法。
- 这部分实现创建的函数的实现并没有caller 以及会在 get,set或者deletion上抛出TypeError错误的 arguments 属性这两个不可改变的“毒药” 。(假如环境支持{jsxref("Object.defineProperty")}}, 或者实现支持__defineGetter__ and__defineSetter__ 扩展)
- 这部分实现创建的函数有 prototype 属性。(正确的绑定函数没有的)
- 这部分实现创建的绑定函数所有的 length 属性并不是同ECMA-262标准一致的:它的 length 是0,而在实际的实现中根据目标函数的 length 和预先指定的参数个数可能会返回非零的 length。
如果你选择使用这部分实现,
你不能依赖于ECMA-262,但是ECMA-5是可以的。
在某些情况下(也可以作另一番修改以适应特定的需要),这部分实现也许可以作为一个过渡,在
bind()
函数被广泛支持之前
Function. prototype. call() & apply()
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
注意:该方法的作用和apply() 方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
语法:
fun.call(thisArg[, arg1[, arg2[, ...]]])
参数:
thisArg:在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
arg1, arg2, …:指定的参数列表。
返回值:
返回结果包括指定的this值和参数。
描述:
可以让call()中的对象调用当前对象所拥有的function。你可以使用call()来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
使用call方法调用父构造函数
在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承,类似于Java中的写法。下例中,使用 Food 和 Toy 构造函数创建的对象实例都会拥有在 Product 构造函数中添加的 name 属性和 price 属性,但 category 属性是在各自的构造函数中定义的。
function Product(name, price) {
this.name = name;
this.price = price;
if (price < 0) {
throw RangeError('Cannot create product ' +
this.name + ' with a negative price');
}
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
//等同于
function Food(name, price) {
this.name = name;
this.price = price;
if (price < 0) {
throw RangeError('Cannot create product ' +
this.name + ' with a negative price');
}
this.category = 'food';
}
//function Toy 同上
function Toy(name, price) {
Product.call(this, name, price);
this.category = 'toy';
}
var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);
使用call方法调用匿名函数
在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通参数就可以),目的是为了演示call的用法。
var animals = [
{species: 'Lion', name: 'King'},
{species: 'Whale', name: 'Fail'}
];
for (var i = 0; i < animals.length; i++) {
(function (i) {
this.print = function () {
console.log('#' + i + ' ' + this.species + ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
使用call方法调用函数并且指定上下文的'this'
在下面的例子中,当调用 greet 方法的时候,该方法的 this 值会绑定到 i 对象。
function greet() {
var reply = [this.person, 'Is An Awesome', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'Douglas Crockford', role: 'Javascript Developer'
};
greet.call(i); // Douglas Crockford Is An Awesome Javascript Developer
Function. prototype. Apply()方法
apply() 方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。
注意:call()方法的作用和 apply()方法类似,只有一个区别,就是 call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
语法
fun.apply(thisArg, [argsArray])
化简操作函数,reduce() 方法对累加器和数组中的每个元素 (从左到右)应用一个函数,将其减少为单个值。
语法: | arr.reduce(callback, [initialValue]) | ||||
参数: | callback:执行数组中每个值的函数,包含下面四个参数
| ||||
| initialValue:可选项,其值用于第一次调用 callback的第一个参数。 | ||||
返回值 | 函数累计处理的结果 |
示例:
将数组所有项相加
var sum = [0, 1, 2, 3].reduce(function(a, b) {
returna + b;
}, 0);
// sum is 6
或者用箭头函数编写
var total = [ 0, 1, 2, 3 ].reduce(
( acc, cur ) => acc + cur,
0
);
数组扁平化
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(a, b) {
returna.concat(b);
},
[]
);
// flattened is [0, 1,2, 3, 4, 5]
或者用箭头函数编写
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
( acc, cur ) =>acc.concat(cur),
[]
);
计算数组中各个值出现次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function(allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
returnallNames;
}, {});
// countedNames is {'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
兼容旧环境(Polyfill)
Array.prototype.reduce 被添加到 ECMA-262标准第5版;因此可能在某些实现环境中不被支持。可以将下面的代码插入到脚本开头来允许在那些未能原生支持 reduce 的实现环境中使用它。
// Production steps of ECMA-262,Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, 'reduce', {
value: function(callback/*, initialValue*/) {
if (this === null) {
thrownew TypeError( 'Array.prototype.reduce' +
'called on null or undefined' );
}
if (typeofcallback !== 'function') {
thrownew TypeError( callback +
' is not a function');
}
// 1. Let Obe ? ToObject(this value).
var o= Object(this);
// 2. Letlen be ? ToLength(? Get(O, "length")).
varlen = o.length>>> 0;
// Steps 3,4, 5, 6, 7
var k= 0;
varvalue;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while(k <len && !(k in o)) {
k++;
}
// 3. If lenis 0 and initialValue is not present,
// throw aTypeError exception.
if (k >= len) {
thrownew TypeError( 'Reduceof empty array ' +
'with no initial value' );
}
value = o[k++];
}
// 8.Repeat, while k < len
while(k <len) {
// a. Let Pk be ! ToString(k).
// b. Let kPresent be ? HasProperty(O, Pk).
// c. If kPresent is true, then
// i. Let kValue be ? Get(O, Pk).
// ii. Letaccumulator be ? Call(
// callbackfn, undefined,
// «accumulator, kValue, k, O »).
if (k in o) {
value = callback(value, o[k], k, o);
}
// d.Increase k by 1.
k++;
}
// 9. Returnaccumulator.
returnvalue;
}
});
}
Function.prototype.reduceRight
reduceRight() 方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
Array. prototype. map()
map()方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
语法: | arr.reduce(callback, [initialValue]) | |||
参数: | callback:生成新数组元素的,包含下面三个参数
| |||
| thisArg:可选项,执行callback函数时使用的this值。 | |||
返回值 | 函数累计处理的结果 |
示例:
// 使用三个参数
const numbers = [1, 2, 3, 4, 5];
let arr =numbers.map((currentValue, index, array) => {
console.log(`currentValue = \n`,currentValue);
console.log(`index = \n`, index);
console.log(`array= \n`, array);
returncurrentValue * 2;
}, numbers);
console.log(`arr \n`,arr);
let numbers = [1, 5, 10, 15];
let doubles = numbers.map((x) => {
returnx * 2;
});
// doublesis now [2, 10, 20, 30]
// numbers is still [1,5, 10, 15]
let numbers = [1, 4, 9];
let roots =numbers.map(Math.sqrt);
// roots isnow [1, 2, 3]
// numbers is still [1,4, 9]
Array. prototype. keys() & values()
keys() 方法返回一个新的Array迭代器,它包含数组中每个索引的键。
let arr = ["a", "b", "c"];
let iterator = arr.keys();
// undefined
console.log(iterator);
// Array Iterator {}
console.log(iterator.next());
// Object {value: 0,done: false}
console.log(iterator.next());
// Object {value: 1,done: false}
console.log(iterator.next());
// Object {value: 2,done: false}
console.log(iterator.next());
// Object {value:undefined, done: true}
values() 方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值。
PS: Chrome未实现
使用 for...of 循环进行迭代
let arr = ['w', 'y', 'k', 'o', 'p'];
let eArr = arr.values();
//您的浏览器必须支持 for..of 循环
//以及 let —— 将变量作用域限定在 for 循环中
for (let letter of eArr) {
console.log(letter);
}
另一种迭代方式
let arr = ['w', 'y', 'k', 'o', 'p'];
let eArr = arr.values();
console.log(eArr.next().value); // w
console.log(eArr.next().value); // y
console.log(eArr.next().value); // k
console.log(eArr.next().value); // o
console.log(eArr.next().value); // p
Function. prototype. bind()
bind()方法是创建一个新的函数,当被调用时,将this关键字设置为提供的值thisArg(即改变调用方法的上下文this),以及在调用新函数时,在已经存在的实参之前添加新加的参数列表[arg1,arg2]。
注意:该方法的是直接返回新的函数,执行还需要加括号(),而call和apply是直接执行。
语法: | fun.bind(thisArg[, arg1[, arg2[, ...]]]) |
参数: | thisArg -当绑定函数被调用时,该参数会作为原函数运行时的 this指向。当使用new操作符调用绑定函数时,该参数无效 |
| arg1, arg2, … -当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。 |
返回值: | 返回由指定的this值和初始化参数改造的原函数拷贝。 |
描述: | bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。 |
用法举例
创建绑定函数
bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都用给定的 this值(上下文)。避免有些情况:将一个方法从对象中拿出来,然后再调用,this变成调用的对象或者是全局对象window,影响运行结果。
this.x = 9;
var module ={
x: 81,
getX: function() { returnthis.x; }
};
module.getX(); // 返回 81
varretrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
偏函数(Partial Functions)
bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
functionlist() {
return Array.prototype.slice.call(arguments);
}
varlist1 = list(1, 2, 3); // [1, 2, 3]
//Create a function with a preset leading argument
varleadingThirtysevenList = list.bind(undefined, 37);
varlist2 = leadingThirtysevenList(); // [37]
varlist3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
配合 setTimeout
在默认情况下,使用 window.setTimeout()时,this 关键字会指向 window(或全局)对象。当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例。
functionLateBloomer() {
this.petalCount = Math.ceil(Math.random() *12) + 1;
}
//Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom= function() {
window.setTimeout(this.declare.bind(this), 1000);
//this表示LateBloomer对象,不执行bind会默认内部this为window
};
LateBloomer.prototype.declare= function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
varflower = new LateBloomer();
flower.bloom(); // 一秒钟后, 调用'declare'方法
快捷调用
在你想要为一个需要特定的 this值的函数创建一个捷径(shortcut)的时候,bind()方法也很好用。
你可以用 Array.prototype.slice来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以创建这样一个捷径:
varslice = Array.prototype.slice;
//...
slice.apply(arguments);
用 bind()可以使这个过程变得简单。在下面这段代码里面,slice是Function.prototype的 call()方法的绑定函数,并且将 Array.prototype的 slice()方法作为 this的值。这意味着我们压根儿用不着上面那个 apply()调用了。
//same as "slice" in the previous example
varunboundSlice = Array.prototype.slice;
varslice = Function.prototype.call.bind(unboundSlice);
//...
slice(arguments);
Polyfill(兼容旧浏览器)
bind 函数在 ECMA-262第五版才被加入;它可能无法在所有浏览器上运行。你可以部份地在脚本开头加入以下代码,就能使它运作,让不支持的浏览器也能使用 bind() 功能。
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeofthis !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
thrownew TypeError("Function.prototype.bind - what is trying to be boundis not callable");
}
varaArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
returnfToBind.apply(this instanceof fNOP
? this
: oThis || this,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype= newfNOP();
returnfBound;
};
}
上述算法和实际的实现算法还有许多其他的不同(尽管可能还有其他不同之处,却没有那个必要去穷尽):
- 这部分实现依赖于Array.prototype.slice(), Array.prototype.concat(), Function.prototype.call()这些原生方法。
- 这部分实现创建的函数的实现并没有caller 以及会在 get,set或者deletion上抛出TypeError错误的 arguments 属性这两个不可改变的“毒药” 。(假如环境支持{jsxref("Object.defineProperty")}}, 或者实现支持__defineGetter__ and__defineSetter__ 扩展)
- 这部分实现创建的函数有 prototype 属性。(正确的绑定函数没有的)
- 这部分实现创建的绑定函数所有的 length 属性并不是同ECMA-262标准一致的:它的 length 是0,而在实际的实现中根据目标函数的 length 和预先指定的参数个数可能会返回非零的 length。
Function. prototype. call() & apply()
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
注意:该方法的作用和apply() 方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
语法: | fun.call(thisArg[, arg1[, arg2[, ...]]]) |
参数: | thisArg:在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。 |
| arg1, arg2, …:指定的参数列表。 |
返回值: | 返回结果包括指定的this值和参数。 |
描述: | 可以让call()中的对象调用当前对象所拥有的function。你可以使用call()来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。 |
使用call方法调用父构造函数
在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承,类似于Java中的写法。下例中,使用 Food 和 Toy 构造函数创建的对象实例都会拥有在 Product 构造函数中添加的 name 属性和 price 属性,但 category 属性是在各自的构造函数中定义的。
function Product(name, price) {
this.name = name;
this.price = price;
if (price < 0) {
throw RangeError('Cannot create product ' +
this.name + ' with a negative price');
}
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
//等同于
function Food(name, price) {
this.name = name;
this.price = price;
if (price < 0) {
throw RangeError('Cannot create product ' +
this.name + ' with a negative price');
}
this.category = 'food';
}
//function Toy 同上
function Toy(name, price) {
Product.call(this, name, price);
this.category = 'toy';
}
var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);
使用call方法调用匿名函数
在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通参数就可以),目的是为了演示call的用法。
var animals = [
{species: 'Lion', name: 'King'},
{species: 'Whale', name: 'Fail'}
];
for (var i = 0; i < animals.length; i++) {
(function (i) {
this.print = function () {
console.log('#' + i + ' ' + this.species + ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
使用call方法调用函数并且指定上下文的'this'
在下面的例子中,当调用 greet 方法的时候,该方法的 this 值会绑定到 i 对象。
function greet() {
var reply = [this.person, 'Is An Awesome', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'Douglas Crockford', role: 'Javascript Developer'
};
greet.call(i); // Douglas Crockford Is An Awesome Javascript Developer
Function. prototype. Apply()方法
apply() 方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。
注意:call()方法的作用和 apply()方法类似,只有一个区别,就是 call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
语法 | fun.apply(thisArg, [argsArray]) |