当一个函数被保存为对象的一个属性时,咱们称它为一个方法
。当一个方法被调用时,this 被绑定到该对象
。若是调用表达式包含一个提取属性的动做(即包含一个.
点表达式或[subscript]
下标表达式),那么它就是被当作一个方法来调用。编程
var myObj = { value: 0, increment: function (inc) { this.value += typeof inc === 'number' ? inc : 1; } }; myObj.increment(); console.log(myObj.value); // 1 myObj.increment(2); console.log(myObj.value); // 3
var add = function (a,b) { return a + b; }; var sum = add(3,4); // sum的值为7
以此模式调用函数时,this 被绑定到全局对象
。数组
延伸:调用内部函数时,如何把 this 绑定到外部函数的 this 变量上浏览器
// 承接上面代码 // 给 myObj 增长一个 double 方法 myObj.double = function() { var that = this; // 解决方法 var helper = function () { console.log(this); // this指向全局对象,若是写成this.value = add(this.value, this.value); 就获取不到正确的结果了 that.value = add(that.value, that.value); }; helper(); // 以函数的形式调用 helper }; myObj.double(); // 以方法的形式调用 double console.log(myObj.value); // 6
var Quo = function (string) { this.status = string; } var myQuo = new Quo("confused"); // 构造一个 Quo 实例 console.log(myQuo.status); // "confused"
一个函数,若是建立的目的就是但愿结合new
前缀来调用,那它就被称为构造(器)函数
,函数内部的this 指向新建立的实例
。app
js 是一门函数式的面向对象编程语言,函数也是一个对象,因此函数能够拥有本身的方法,apply、call就是其中的两种方法。编程语言
此种调用模式容许咱们能够显式地设置 this 的指向
,具体使用见下文。函数
1. fun.apply(thisArg[, argsArray])
在指定 this 值和参数(参数以数组
或类数组
对象的形式存在)的状况下调用某个函数。thisArg
:在 fun 函数运行时指定的 this 值,若是这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象)argsArray
:一个数组或者类数组对象,其中的数组元素将做为单独的参数传给 fun 函数。若是该参数的值为null 或 undefined,则表示不须要传入任何参数。也可使用 arguments 对象做为 argsArray 参数,用arguments把全部的参数传递给被调用对象。oop
/* 求一个数组中的最大最小值 */ var numbers = [5, 6, 2, 3, 7]; /* simple loop based algorithm */ 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]; } /* vs. using Math.min/Math.max apply */ var max = Math.max.apply(null, numbers); /* This about equal to Math.max(numbers[0], ...) or Math.max(5, 6, ..) */ var min = Math.min.apply(null, numbers);
从上面的例子能够看到:原本须要写成遍历数组变量的任务,apply使用内建的函数就完成了。据此,能够简化某些对数组的操做this
var arr1 = [1,2,3]; var arr2 = [4,5,6]; /* 若是咱们要把 arr2 展开,而后一个一个追加到 arr1 中去,最后让 arr1=[1,2,3,4,5,6] * arr1.push(arr2)是不行的,由于这样作会获得[1,2,3,[4,5,6]] * 能够循环arr2,而后一个一个的push,可是这样比较麻烦,使用apply,就so easy了 */ Array.prototype.push.apply(arr1,arr2); console.log(arr1); // [1,2,3,4,5,6] /* 也能够用arr1.concat(arr2),可是concat方法返回的是一个新数组,并不改变arr1自己 */
2. fun.call(thisArg[, arg1[, arg2[, ...]]])
该方法的做用和 apply() 方法相似,只有一个区别,就是 call() 方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组。prototype
Math.max.apply(null, [1,2,3,4]); Math.max.call(null, 1, 2, 3, 4); /* eg. 使用call方法调用父构造函数 */ function Animal(name){ this.name = name; this.showName = function(){ console.log(this.name); } } function Cat(name){ Animal.call(this, name); // 此行代码中的this指向Cat的实例 } var cat = new Cat("Black Cat"); cat.showName(); // "Black Cat"
3. fun.bind(thisArg[, arg1[, arg2[, ...]]])
当在函数fun上调用bind( )方法并传入一个对象thisArg做为参数,这个方法将返回一个新函数。调用新的函数将会把原始的函数fun当作thisArg的方法来调用。thisArg
:当绑定函数被调用时,该参数会做为原函数运行时的 this 指向。当使用new 操做符
调用绑定函数时,该参数无效
arg1, arg2, ...
:当绑定函数被调用时,这些参数加上绑定函数自己的参数会按照顺序做为原函数运行时的参数code
// eg.1 var sum = function (x,y) { return x + y; }; var succ = sum.bind(null, 1); succ(2); // => 3: x绑定到1,并传入2做为实参y // eg.2 function f(y,z) { return this.x + y + z; }; var g = f.bind({x:1}, 2); // 绑定this和y g(3); // =>6: this.x绑定到1,y绑定到2,z绑定到3 // eg.3 建立绑定函数 this.x = 9; var module = { x: 81, getX: function() { return this.x; } }; module.getX(); // 81 var retrieveX = module.getX; retrieveX(); // 9, because in this case, "this" refers to the global object // Create a new function with 'this' bound to module var boundGetX = retrieveX.bind(module); boundGetX(); // 81
bind 函数在ES5版本中才被加入,ES3版本的bind( )方法实现以下(js权威指南p191):
if (!Function.prototype.bind) { Function.prototype.bind = function(o[, args]) { var self = this, boundArgs = arguments; // bind()方法的返回值是一个函数 return function() { // 建立一个实参列表,将传入bind()的第二个及后续的实参都传入这个函数 var args = [], i; for(i = 1; i < boundArgs.length; i++) args.push(boundArgs[i]); for(i = 0; i < arguments.length; i++) args.push(arguments[i]); // 如今将self做为o的方法来调用,传入这些实参 return self.apply(o, args); } } } /* 关键点有二:一是改变this的指向,二是改变传入参数的个数 * * 上述代码并未实现ES5中bind方法的所有特性,但思路比较清晰明了,且知足大部分需求了 */