重要
JavaScript的
this
是有函数求值是的调用者决定的JavaScript的
this
是有函数求值是的调用者决定的JavaScript的
this
是有函数求值是的调用者决定的
函数中的this
函数中的this
在调用时才有意义,函数的调用者决定函数中this
的指向,每个函数调用时都会有this
属性。
function Point2D(x, y) {
this.x = x;
this.y = y;
}
Point2D.prototype.showLength = function() {
var length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
console.log(length);
};
Point2D.prototype.showLengthAsync = function() {
var self = this; // 将this的值保存下来,传入异步函数
setTimeout(function() { // setTimeout是异步函数,在调用回调函数时没有指定匿名函数的this,
self.showLength(); //因为回调函数没有指定this,非严格模式下默认指向全局对象
}, 1000);
};
var x = 30, y = 40;
var p = new Point2D(3, 4);
var f = Point2D.prototype.showLength;
1.f()
:输出50
,因为函数Point2D.prototype.showLength
的调用者是变量f
,变量f
、x
、y
、Ponit2D
函数在同一个对象中。
所以函数
Point2D.prototype.showLength
中this
指向的是包含变量f
、x
、y
、Ponit2D
函数的对象。this.x = 30
,this.y = 40
,所以f()
是输出50;
Point2D.prototype.showLength = function() {
var length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
console.log(length);
};
2.setTimeout(p.showLength, 500);
:延时500ms后输出50
。函数p.showLength
的调用者是异步函数setTimeout
,setTimeout
不会为回调函数指定this
值。
在非严格模式下,
p.showLength
中的this
默认指向全局变量this.x = 30
,this.y = 40
,所以setTimeout(p.showLength, 500)
延时500ms后输出50;-
借用
bind()
方法可以将函数绑定到对象上,即将函数内的this
指向bind(p)
方法中传入的p
对象setTimeout(p.showLength.bind(p), 500); // 延时500ms后输出5,因为函数p.showLength指向对象p,p的x属性为3,y属性为4
3.p.showLengthAsync();
: 延时1000ms后输出5
。
Point2D.prototype.showLengthAsync = function() {
var self = this; // 将this的值保存下来,传入异步函数
setTimeout(function() { // setTimeout是异步函数,在调用回调函数时没有指定匿名函数的this,
self.showLength(); //因为回调函数没有指定this,非严格模式下默认指向全局对象
}, 1000);
};
p.showLengthAsync
函数的调用者是p
对象本身,由于setTimeout
不会为回调函数指定this
值。所以在p.showLengthAsync
函数中使用变量self
将p.showLengthAsync
函数调用的this
值(即对象p
)保存下来回调函数中才可以利用变量
self
访问到对象p
,完成正确调用;
4.箭头函数
箭头函数很特殊,箭头函数中没有定义
this
,所以可以使用外层函数的this
。
Point2D.prototype.showLengthAsync = function() {
setTimeout( () => this.showLength(), 1000); // 箭头函数没有定义this,可以访问外层函数的this
};
call()
函数的call()
方法可以指定函数调用时的this
值。函数中调用时的this
值是可以改变的
-
并且,
call()
方法可以从第二个参数开始,指定传入调用函数的参数,以逗号分隔(多个原始形式的参数)function Point2D(x, y) { this.x = x; this.y = y; } Point2D.prototype.showLength = function() { var length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); console.log(length); }; var p = new Point2D(1, 1); console.log(p.showLength()); // ==> 1.4142... 调用p.length方法的是p对象本身,所以this.x和this.y均等于1 var obj = {x: 3, y: 4}; console.log(p.showLength.call(obj)); // ==> 5 使用函数p.showLength的call()方法改变函数调用时this的指向, //使其指向对象obj,所以this.x=3,this.y=4 function foo() { // 使用Array.prototype.slice函数的call()方法指定函数调用时的this指向arguments对象,将其切分为数组 var args = Array.prototype.slice.call(arguments); console.log(Array.isArray(arguments)); // false console.log(Array.isArray(args)); // true }
call()
的作用:改变当前函数调用时的this
值
apply()
函数的apply()
方法与call()
方法作用完全一致,只是在调用时传入的参数有区别:
call()
方法:第一个参数接收新的this
值,后面的参数逗号分隔,逐个排列,传入调用函数中-
apply()
方法:第一个参数接收新的this
值,第二个参数接收一个数组,将数组整体作为参数传入调用函数中// 定义一个函数,实现一种变换,将传入其中的函数中参数的顺序颠倒 function __reverseArgs__(fn) { return function() { var args = Array.prototype.slice.call(arguments); return fn.apply(this, args.reverse()); // 这里的this表示这个匿名函数的调用者 }; } var foo = function() {console.log(Array.from(arguments));}; var foo2 = __reverseArgs__(foo); foo2(1, 2, 3, 4);
注意
return fn.apply(this, args.reverse());
中的this
。因为__reverseArgs__(fn)
函数返回一个新的函数,apply()
中的this
指向调用这个新函数的对象。foo2
接收了__reverseArgs__(foo)
返回的新函数,调用foo2(1, 2, 3, 4)
时,因为foo2
在全局对象下,所以this
的值是全局对象。__reverseArgs__(fn)
方法应该只改变传入函数中参数的顺序,不改变原来函数调用的作用域。所以使用this
将函数的调用改回传入函数的作用域。
bind()
bind()
方法与call()
和apply()
方法的最大区别在于返回值:call()
和apply()
都会立即执行,返回结果;而bind()
方法会返回一个函数,并且可以通过bind()
向函数中传递已经确定的参数,对于异步调用很有帮助。
function add(x, y) {
return x + y;
}
// call()和apply()会立即执行;bind()方法会返回一个函数对象
console.log(add.call(null, 1, 2)); // ==> 3 ,使用call()方法在全局对象下对传入的参数1,2执行函数
console.log(add.apply(null, [1, 2])); // ==> 3 apply()方法在全局对象下对传入的参数1,2执行函数
console.log(add.bind(null, 1, 2)); // ==> function () { [native code] }, 返回一个函数
// 传入两个参数
let add1 = add.bind(null, 1, 2); // bind()方法执行时,将1和2对应传递给x,y,返回一个函数。再调用返回的函数时,不用再传递参数
console.log(add1()); // ==> 3 为传递参数,直接调用,使用bind()时传入的参数值
// 传入一个参数
let add2 = add.bind(null, 1); //bind()时只传递一个参数1给x,返回一个函数,在调用返回函数时只需炫迪一个参数给y即可
console.log(add2(3)); // ==> 4 只需传递一个参数给y即可
// 不传递参数
let add3 = add.bind(null); //调用bind()时不传递参数,返回一个函数,在调用返回函数时要传递2个参数
console.log(add3(2, 3)); // ==> 5 需要传递两个参数,与调用原函数没有区别。。。不建议使用
可以看出bind()
的最大用处在于返回一个函数,并且可以向函数内传递确定的参数值(在bind()
执行时时便已经确定的参数)。调用返回的函数时,无需再传入bind()
时传入的参数---实现函数的部分调用
应用场景:类似于setTimeout(fn, 1000);
的异步函数,需要一个函数作为参数,在1s后执行。有时对于已经知道fn
调用时传入的参数值时,便可以使用bind()
,先将参数传递进去,返回一个函数;等待时间到后,无需再向该函数传入参数,立即执行即可
function setBodyState(state) {
document.body.className = state;
}
setBodyState.call(null, 'state1'); // 立即执行setBodyState函数,将document.body.className设置为'state1'。
// this值为null表示在全局对象下执行该函数
setTimeout(setBodyState.bind(null, 'state2'), 1500); // 执行bind()方法,返回一个函数作为回调函数,并且将需要向它传递
//的'state2'作为参数。1500ms后立即执行返回的函数即可,无需再传入参数
bind()
方法在异步函数中的应用,主要由于其返回一个函数,并且可以传入参数值的特性,可以减少异步函数调用的部分问题。
高阶函数实例
Closure中访问外部函数的arguments
、this
、形参和局部变量
// __multi__()抽象一个过程,将传入的函数进行扩展,使其第一个参数接收类数组
// 调用原来的方法fn对每个第一个数组参数中的每个元素执行fn方法
function __multi__(fn) {
return function(arrayLike, ...args) {
return Array.from(arrayLike).map(item => fn(item, ...args));
};
}
function __multi__(fn) {
return function(arrayLike, ...args) { // 返回一个函数(创建一个Closure),返回的函数接收夜歌类数组对象和rest参数作为参数
return Array.from(arrayLike).map(function(value) {
return fn(value, ...args);
});
};
}
function add(x, y) { return x + y;}
var add2 = __multi__(add);
console.log(add2([1,2,3], 4)); // ==> [5, 6, 7]
1.注意map()
方法如果需要返回值,一定要在传入map
()的函数中返回值,因为默认的返回值是
undefined`
2.Closure中对于外层函数的局部变量、形参、实参对象arguments
和this
的访问问题:
__multi__()
返回一个函数(创建一个Closure),返回的函数保留对外层函数作用域的访问能力但
this
值和实参对象arguments
是根据调用函数时确定的,如果要在Closure中访问到调用时的this
和arguments
对象,需要将其保存在外部函数局部作用域的变量中。
function __multi__(fn) {
var self = this;
var outerArgs = arguments;
return function(arrayLike, ...args) {
return Array.from(arrayLike).map(function(value) {
console.log(self); // 可以访问到外层函数的调用者
console.log(outerArgs); //可以访问到外层函数的实参列表,相当于访问外层函数的局部变量
console.log(this); // 访问到的this值是返回函数的调用者
console.log(arguments); // 访问到的arguments是传入返回函数的实参
return fn(value, ...args); // 可以访问到外部函数的形参
});
};
}
-
Closure中可以访问到外部函数的形参(形参的特性与局部变量相同)
function __multi__(fn, a = 2) { console.log(arguments[0]); return function(arrayLike, ...args) { return Array.from(arrayLike).map(function(value) { console.log(a); // 可以访问到外层函数的形参a return fn(value, ...args); // 可以访问到外部函数的形参fn }); }; }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。