重要 数组
JavaScript的
this
是有函数求值是的调用者决定的appJavaScript的
this
是有函数求值是的调用者决定的异步JavaScript的
this
是有函数求值是的调用者决定的函数
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
函数在同一个对象中。prototype
因此函数Point2D.prototype.showLength
中this
指向的是包含变量f
、x
、y
、Ponit2D
函数的对象。rest
this.x = 30
,this.y = 40
,因此f()
是输出50;code
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
默认指向全局变量ip
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()
方法在异步函数中的应用,主要因为其返回一个函数,而且能够传入参数值的特性,能够减小异步函数调用的部分问题。
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 }); }; }