作用:这三个函数的作用是改变函数执行时的上下文,说的再具体一点就是改变函数运行时的this指向。
function Person(name) {
this.name = name
}
Person.prototype = {
showName: function () {
console.log(this.name)
}
}
var person = new Person('xiaogang')
person.showName() // xiaogang
var animal = {
name: 'dog'
}
person.showName.call(animal) // dog 此时执行showName时this指向animal
person.showName.apply(animal) // dog 此时执行showName时this指向animal
person.showName.bind(animal)() // dog 此时执行showName时this指向animal
animal这个对象本身并没有showName方法,我们可以通过call、apply、bind来拿person这个实例的showName方法,并动态改变其上下文来帮自己输出信息,实现了复用
区别
call()
call() 方法在使用一个指定的this值和若干个指定的参数值得前提下调用某个函数或方法
当调用一个函数时,可以赋值一个不同的this对象。this引用当前对象,即call方法的第一个参数。
通过call方法,你可以在一个对象上借用另一个对象的方法,比如
Object.prototype.toString.call([]),就是一个Array对象借用了Object对象上的方法
语法 fun.call(thisArg[,arg1[,arg2[,...]])
thisArg
在fun函数运行时指定的this值。需要注意的是下面几种情况
- 不传,或者传null,undefined,函数中的this指向window对象
- 传递另一个函数的函数名,函数中的this指向这个函数的引用,并不一定是该函数执行时真正的this值
- 值为原始值(数字,字符串,布尔值)的this会指向该原始值得自动包装对象,如String、Number、Boolean
- 传递一个对象,函数中的this指向这个对象
function a() {
console.log(this)
}
function b() { }
var c = { name: '小红' }
a.call() // window
a.call(null) // window
a.call(undefined) // window
a.call(1) // Number
a.call('') // String
a.call(false) // Boolean
a.call(b) // function b(){}
a.call(c) // {name: '小红'}
使用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',
say: function () {
console.log("Hi,I'm function a")
}
}
function b(name,test) {
console.log('post params:' + name + ' params1:'+ test)
console.log("I'm" + this.name)
this.say()
}
b.call(a, 'test','test1')
// post params:test params1:test1
// I'mJSLite.io
// Hi,I'm function a
apply()
语法与call()方法的语法几乎完全相同,唯一的区别在于,apply的第二个参数必须是一个包含多个参数的数组(或类数组对象)。apply的这个特性很重要,在调用一个存在的函数时,你可以为其指定一个this对象。this指当前对象,也就是正在调用这个函数的对象。使用apply,你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。
语法: fun.apply(thisArg[,argsArray])
注意:chrome 14以及 IE9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常
thisArg
同上 call 的 thisArg 参数
argsArray
一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。如果该参数的值为null或者undefined,则表示不需要传入仁和参数。从ES 5开始可以使用类数组对象
function jsy(x, y, z) {
console.log(x, y, z)
console.log(this.name)
}
var a = { name: '678' }
jsy.apply(a, [1, 2])
// 1 2 undefined
// 678
使用apply来链接构造器的例子
你可以使用apply来给一个对象链接构造器,类似于Java。在接下来的例子中我们会创建一个叫做construct的全局Function函数,来使你能够在构造器中使用一个类数组对象而非参数列表
Function.prototype.construct = function (aArgs) {
var fConstructor = this
var fNewConstr = function () {
fConstructor.apply(this, aArgs)
}
fNewConstr.prototype = fConstructor.prototype
return new fNewConstr()
}
function MyConstructor() {
for (var nProp = 0; nProp < arguments.length; nProp++) {
this['property' + nProp] = arguments[nProp]
}
}
var myArray = [4,'Hello world', false]
var myInstance = MyConstructor.construct(myArray)
console.log(myInstance.property1) // Hello wold
console.log(myInstance instanceof MyConstructor) // true
console.log(myInstance.constructor) // f Myconstructor
使用apply和内置函数
聪明的apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数。在接下来的例子中我们会使用Math.max/Math.min来找出一个数组中的最大/最小值
var numbers = [5, 6, 2, 3, 7]
var max = Math.max.apply(null, numbers)
var min = Math.min.apply(null, numbers)
console.log(max) // 7
console.log(min) // 2
//通常情况下我们会这样来找数组的最大或最小值
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]
}
}
// 或者使用最新的扩展语句spread operator
var arr = [1, 2, 3]
var max = Math.max(...arr)
console.log(max) // 3
参数数组切块后循环传入
function minOfArray(arr) {
var min = Infinity
var QUANTUM = 32768
for (var i = 0; i < arr.length; i += QUANTUM) {
var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, arr.length)))
console.log(submin, min)
min = Math.min(submin, min)
}
return min
}
var min = minOfArray([5, 6, 2, 3, 7])
console.log(min) // 2
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.addEventListener('click', EventClick.bind(obj, 'p1', 'p2'), false)
function EventClick(a, b) {
console.log(this.name, a, b)
}
// JSLite.io p1 p2
call、apply与bind的差别
call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数
call、apply的区别
他们俩之间的差别在于参数的区别,call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。
fn.call(obj, arg1, arg2, arg3...)
fn.apply(obj, [arg1,arg2,arg3...])
应用
知道了怎么使用和他们之间的区别,接下来我们来了解一下通过call、apply、bind常见应用场景。
继承
function Animal(name, weight) {
this.name = name
this.weight = weight
}
function Cat(arr) {
// 在call中将this作为thisArgs参数传递
// Animal方法中的this就指向了Cat中的this
// 所以Animal中的this指向的就是cat对象
// 在Animal中定义了name和weight属性,就相当于在cat中定义了这些属性
// cat对象便拥有了Animal中定义的属性,从而达到了继承的目的
// Animal.apply(this, arr)
Animal.call(this, ...arr)
this.say = function () {
console.log('I am ' + this.name + ', my weight is ' + this.weight)
}
}
// 当通过new运算符产生了cat时,Cat中的this就指向了cat对象
var cat = new Cat(['doudou', '40'])
cat.say()
// I am doudou, my weight is 40
原型拓展
在原型函数上扩展和自定义方法,从而不污染原生函数。例如:我们在Array上扩展一个forEach
function test() {
console.log(arguments instanceof Array, Array.isArray(arguments)) // false false
console.log(arguments.forEach) // undefined
Array.prototype.forEach.call(arguments, function (item) {
console.log(item)
})
}
test(1, 2, 3, 4)
// 1
// 2
// 3
// 4