在JavaScript中,call
、apply
和bind
是Function
对象自带的三个要领,本文将经由过程几个场景的运用,来细致明白三个要领。
call()
call()
要领在运用一个指定的this值和若干个指定的参数值的前提下挪用某个函数或要领。
当挪用一个函数时,可以赋值一个差别的 this
对象。this
援用当前对象,即 call
要领的第一个参数。
经由过程 call
要领,你可以在一个对象上借用另一个对象上的要领,比方Object.prototype.toString.call([])
,就是一个Array
对象借用了Object
对象上的要领。
语法
fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
在fun函数运行时指定的this
值。须要注重的是下面几种状况
(1)不传,或许传null
,undefined
, 函数中的this
指向window对象
(2)通报另一个函数的函数名,函数中的this
指向这个函数的援用,并不一定是该函数实行时真正的this
值
(3)值为原始值(数字,字符串,布尔值)的this
会指向该原始值的自动包装对象,如 String
、Number
、Boolean
(4)通报一个对象,函数中的this指向这个对象
arg1, arg2, …
指定的参数列表。
例子
低级运用例子
function a(){
//输出函数a中的this对象
console.log(this);
}
//定义函数b
function b(){}
var obj = {name:'这是一个屌丝'}; //定义对象obj
a.call(); //window
a.call(null); //window
a.call(undefined);//window
a.call(1); //Number
a.call(''); //String
a.call(true); //Boolean
a.call(b);// function b(){}
a.call(obj); //Object
运用call要领挪用匿名函数而且指定上下文的this
鄙人面的例子中,当挪用 greet
要领的时刻,该要领的 this
值会绑定到 i
对象。
function greet() {
var reply = [this.person, '是一个轻量的', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'JSLite.io', role: 'Javascript 库。'
};
greet.call(i);
// JSLite.io 是一个轻量的 Javascript 库。
运用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);
}
//#0 Lion: King
//#1 Whale: Fail
运用call要领挪用函数传参数
var a = {
name:'JSLite.io', //定义a的属性
say:function(){ //定义a的要领
console.log("Hi,I'm function a!");
}
};
function b(name){
console.log("Post params: "+ name);
console.log("I'm "+ this.name);
this.say();
}
b.call(a,'test');
//Post params: test
//I'm JSLite.io
//I'm function a!
apply()
语法与 call()
要领的语法险些完全相同,唯一的区分在于,apply的第二个参数必需是一个包括多个参数的数组(或类数组对象)。apply
的这个特征很主要,
在挪用一个存在的函数时,你可以为其指定一个 this
对象。 this
指当前对象,也就是正在挪用这个函数的对象。 运用 apply, 你可以只写一次这个要领然后在另一个对象中继续它,而不用在新对象中重复写该要领。
语法:
fun.apply(thisArg[, argsArray])
注重: 须要注重:Chrome 14
以及 Internet Explorer 9
依然不接受类数组对象。假如传入类数组对象,它们会抛出非常。
参数
thisArg
同上call
的thisArg
参数。
argsArray
一个数组或许类数组对象,个中的数组元素将作为零丁的参数传给 fun
函数。假如该参数的值为null
或 undefined
,则示意不须要传入任何参数。从ECMAScript 5
最先可以运用类数组对象。
例子
function jsy(x,y,z){
console.log(x,y,z);
}
jsy.apply(null,[1,2,3]);
// 1 2 3
运用apply来链接组织器的例子
你可以运用apply
来给一个对象链接组织器,相似于Java
. 在接下来的例子中我们会建立一个叫做construct
的全局的Function
函数,来使你可以在组织器中运用一个类数组对象而非参数列表。
Function.prototype.construct = function(aArgs) {
var fConstructor = this,
fNewConstr = function() {
fConstructor.apply(this, aArgs);
};
fNewConstr.prototype = fConstructor.prototype;
return new fNewConstr();
};
function MyConstructor () {
for (var nProp = 0; nProp < arguments.length; nProp++) {
console.log(arguments,this)
this["property" + nProp] = arguments[nProp];
}
}
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
console.log(myInstance.property1); // logs "Hello world!"
console.log(myInstance instanceof MyConstructor); // logs "true"
console.log(myInstance.constructor); // logs "MyConstructor"
运用apply和内置函数
智慧的apply用法许可你在某些原本须要写成遍历数组变量的使命中运用内建的函数。在接下里的例子中我们会运用Math.max
/Math.min
来找出一个数组中的最大/最小
值。
//内里有最大最小数字值的一个数组对象
var numbers = [5, 6, 2, 3, 7];
/* 运用 Math.min/Math.max 在 apply 中运用 */
var max = Math.max.apply(null, numbers);
// 平常状况是用 Math.max(5, 6, ..) 或许 Math.max(numbers[0], ...) 来找最大值
var min = Math.min.apply(null, numbers);
//一般状况我们会如许来找到数字的最大或许最小值
//比对上面的栗子,是不是是下面的看起来没有上面的惬意呢?
max = -Infinity, min = +Infinity;
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] > max)
max = numbers[i];
if (numbers[i] < min)
min = numbers[i];
}
参数数组切块后轮回传入
function minOfArray(arr) {
var min = Infinity;
var QUANTUM = 32768;
for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
console.log(submin, min)
min = Math.min(submin, min);
}
return min;
}
var min = minOfArray([5, 6, 2, 3, 7]);
bind
bind() 函数会建立一个新函数(称为绑定函数)
bind是ES5新增的一个要领
传参和call或apply相似
不会实行对应的函数,call或apply会自动实行对应的函数
返回对函数的援用
语法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
下面例子:当点击网页时,EventClick
被触发实行,输出JSLite.io p1 p2
, 申明EventClick
中的this
被bind
转变成了obj
对象。假如你将EventClick.bind(obj,'p1','p2')
变成 EventClick.call(obj,'p1','p2')
的话,页面会直接输出 JSLite.io p1 p2
var obj = {name:'JSLite.io'};
/**
* 给document增加click事宜监听,并绑定EventClick函数
* 经由过程bind要领设置EventClick的this为obj,并通报参数p1,p2
*/
document.addEventListener('click',EventClick.bind(obj,'p1','p2'),false);
//当点击网页时触发并实行
function EventClick(a,b){
console.log(
this.name, //JSLite.io
a, //p1
b //p2
)
}
// JSLite.io p1 p2
兼容
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this, // this在这里指向的是目的函数
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this //此时的this就是new出的obj
: oThis || this,//假如通报的oThis无效,就将fBound的挪用者作为this
//将经由过程bind通报的参数和挪用时通报的参数举行兼并,并作为终究的参数通报
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
//将目的函数的原型对象拷贝到新函数中,由于目的函数有能够被看成组织函数运用
fBound.prototype = new fNOP();
//返回fBond的援用,由外部按需挪用
return fBound;
};
}
运用场景:继续
function Animal(name,weight){
this.name = name;
this.weight = weight;
}
function Cat(){
// 在call中将this作为thisArgs参数通报
// Animal要领中的this就指向了Cat中的this
// 所以Animal中的this指向的就是cat对象
// 在Animal中定义了name和weight属性,就相当于在cat中定义了这些属性
// cat对象便具有了Animal中定义的属性,从而达到了继续的目的
Animal.call(this,'cat','50');
//Animal.apply(this,['cat','50']);
this.say = function(){
console.log("I am " + this.name+",my weight is " + this.weight);
}
}
//当经由过程new运算符产生了cat时,Cat中的this就指向了cat对象
var cat = new Cat();
cat.say();
//输出=> I am cat,my weight is 50
原型扩大
在原型函数上扩大和自定义要领,从而不污染原生函数。比方:我们在 Array
上扩大一个 forEach
function test(){
// 检测arguments是不是为Array的实例
console.log(
arguments instanceof Array, //false
Array.isArray(arguments) //false
);
// 推断arguments是不是有forEach要领
console.log(arguments.forEach);
// undefined
// 将数组中的forEach运用到arguments上
Array.prototype.forEach.call(arguments,function(item){
console.log(item); // 1 2 3 4
});
}
test(1,2,3,4);
关注民众号