this可以动态的取值,实现了隐式传递一个对象的引用
var obj = { a : { b : 2, c : function () { console.log(this.b); } } }
上面的例子中想在c中取b的值用this就能简单搞定,必定程度上实现了动态取值;obj.a.c(),这个this就是绑定了obj.a,这个就是上下文对象也就是调用栈javascript
this始终是一个对象,函数在被调用时发生绑定,具体绑定了什么看调用的位置,有必定的绑定规则java
默认绑定
当函数不带任何修饰被调用时,运用默认绑定,this就是window全局对象;但要注意的是在严格模式下,则不行,this会绑定成undefined数组
function f() { //"use strict" console.log(this.a); } var a = 123; f(); //123
function f() { "use strict" console.log(this.a); } var a = 123; f(); //报错,undefined不可以添加属性
隐式绑定
当上下文对象(调用栈)调用函数时,会隐式绑定this,哪一个对象调用就绑定谁app
function f() { console.log(this.a); } var obj = { a : 123, b : f, } obj.b();
上面例子实际上就是this = obj,而不是this = obj.a
这是最简单的,可是这种绑定经常会发生隐式丢失,而采用默认绑定函数
function f() { console.log(this.a); } var obj = { a : 123, b : f, } var a = 345 var f1 = obj.b; //采起函数别名而发生了隐式丢失 f1(); //345,这等同于直接执行了f函数
个人理解是函数取别名是最好赋值上下文对象,也就是调用栈(var f1 = b)
例以下面例子也是隐式丢失this
function f() { console.log(this.a); } var obj = { a : 123, b : f, } function fn(f) { //等同于setTimeout(obj.b, 100) f(); } fn(obj.b);
要注意这种别名:prototype
function f() { console.log(this.a); } var obj = {a : 1}; obj.fn = f; obj.fn()
实际上就是:code
function f() { console.log(this.a); } var obj = { a : 1, fn : f, }; obj.fn()
call
能够填入多个参数对象
func.call(thisValue, arg1, arg2, arg3,...)
第一个参数是this要绑定的对象,若参数为空,undefined,null,则默认绑定window;若参数为原始值,则this绑定对应的包装类对象
后面参数则是传入func的参数继承
function f(n, m) { console.log(n, m, this.a); } var obj = {a : 1}; f.call(obj, 3, 4); //3 4 1
function f() { console.log(this); } f.call("123"); //String {"123"}
apply
apply与call相似只是参数不一样,就是将传入原函数的参数变成数组形式
func.call(thisValue, [arg1, arg2, arg3,...])
apply与call能够这样理解:就是thisValue借用func函数功能来实现效果
相似于thisValue.fn(arg1, arg2, arg3...)
利用这一特色能够实现有趣的效果
找数组中最大的数
var arr = [11, 2, 3, 4, 5]; var nmax = Math.max.apply(null, arr); //11
null的做用是由于用不到this,用null代替,仅用后面的参数,有柯里化的感受
建议咱们通常定义一个比null还空的空对象:var ø = Object.create(null)
将数组中的空元素变成undefined,这个样就可遍历了,其属性描述的enumerable:true
var arr = [11, , 3, 4, 5]; var arr1 = Array.apply(null, arr); //[11, undefined, 3, 4, 5]
转换类数组对象,利用Array的实例方法slice,前提是必须有length属性
var arr = Array.prototype.slice.apply({0 :1, 1 : 2, length : 3}); //[1, 2, empty] var arr = Array.prototype.slice.apply({0 :1}); //[] var arr = Array.prototype.slice.apply({length : 1}); //[empty]
bind
bind函数参数形式和call相似
func.bind(thisValue, arg1, arg2, arg3,...)
我对他的理解是对原函数进行改造,生成本身的新函数,主要改造就是this绑定成thisValue,而且能够固定部分参数,固然后面arg选填
相似于thisValue.fn
咱们能够自定义一个简单bind函数:
function myBind(fn, obj) { return function () { fn.apply(obj, arguments); } } function f(n) { console.log(n + this.a) } var obj = {a : 1}; var bind = myBind(f, obj); bind(4); //5
原生态bind用法也相似
function f(n,m) { console.log(this.a + n + m); } var obj = {a : 1}; var bar = f.bind(obj, 2); bar(3); //6
注意点bind每次返回一个新函数,在监听事件时要注意,否则romove不掉
element.addEventListener('click', o.m.bind(o)); element.removeEventListener('click', o.m.bind(o));
由于o.m.bind(o)返回的时新函数,因此remove的也不是开启时的函数了
正确作法:添加一个值,记录开启时的函数
var listener = o.m.bind(o) element.addEventListener('click', listener); element.removeEventListener('click', listener);
有趣的地方:
利用call与bind实现原始方法
var slice = Function.prototype.call.bind(Array.prototype.slice); slice([1, 2, 3], 1); //[2, 3]
能够拆分bind看
Array.prototype.slice.Function.prototype.call([1, 2, 3], 1)
其中Function.prototype.call 就是call方法
Array.prototype.slice.call([1, 2, 3], 1)
拆分call
[1, 2, 3].Array.prototype.slice(1)
而Array.prototype.slice就是slice
因此
[1, 2, 3].slice(1)
我的理解能够当作,Array.prototype.slice实现了slice功能,Function.prototype.call实现了arguments中this的绑定以及参数的带入。因此函数最总调用时显示:slice([1, 2, 3], 1);
同理
var push = Function.prototype.call.bind(Array.prototype.push); var pop = Function.prototype.call.bind(Array.prototype.pop);
同时bind也能被改写
function f() { console.log(this.a); } var obj = { a : 123 }; var bind = Function.prototype.call.bind(Function.prototype.bind); bind(f, obj)() // 123
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
隐式绑定与默认绑定比较
function f() { console.log(this.a); } var obj = { a : 123, f : f, } var a = 456; obj.f(); // 123
obj.f()覆盖了f(),所以隐式大于默认
隐式绑定与显示绑定比较
function f() { console.log(this.a); } var obj = { a : 123, f : f, } var obj1 = { a : 1123 } obj.f.call(obj1); //1123
由输出结果能够看出:call绑定的obj1覆盖了obj,因此显示大于隐式
显示绑定与new绑定
function f(n) { this.a = n; console.log(this); } var obj = { b : 2}; var bar = f.bind(obj); console.log(bar(2)); console.log(new bar(2)); //{b:2,a:2} //undfined //{a: 2} //{a: 2}
由输出结果能够看出:new bar(2),{a:2}说明是新生成的空对象,添加了a的属性,其次输出两个说明函数返回了一个this对象也就是{a:2},而不是undefined
因此new大于显示
注意多层this*
var obj = { f: function () { console.log(this); var f1 = function () { //console.log(this); }(); } } obj.f(); //{f:func} //winodw
由于传统的this没有继承机制,因此这个匿名函数的this没有任何修饰,采起默认绑定
有两种方法解决
var obj = { f: function () { var self= this console.log(self); var f1 = function () { //console.log(self); }(); } } obj.f(); //{f:func} //{f:func}
var obj = { f: function () { console.log(this); return () => { console.log(this); }; } } obj.f()(); //{f:func} //{f:func}
注意处理数组时使用this
var obj = { a : 123, arr : [1, 2, 3], f : function () { this.arr.forEach(function (elem) { console.log(this.a, elem); }); } } var a = "this是window"; obj.f(); //this是window 1 //this是window 2 //this是window 3
forEach中的this指向的是window
解决办法:
var obj = { a : 123, arr : [1, 2, 3], f : function () { this.arr.forEach(function (elem) { console.log(this.a, elem); },this); } } var a = "this是window"; obj.f(); //123 1 //123 2 //123 3
还有一种就是赋值给一个变量self暂时保存,给遍历时用,如同上面的self
注意回调函数中的this
var obj = new Object(); obj.f = function () { console.log(this === obj); } $('#button').on('click', obj.f); //false
this指向的是DOM对象而不是obj,这个有点难以察觉,须要注意
解决办法:硬绑定obj对象
var obj = new Object(); obj.f = function () { console.log(this === obj); } function fn(){ obj.f.apply(obj); } $('#button').on('click', fn); //true //$('#button').on('click', o.f.bind(obj)); //true
上面的硬绑定至关于固定了this的对象,不会变了。
咱们能够作个软绑定
if(!Function.prototype.sorfBind) { Function.prototype.sorfBind = function (obj) { //这个this:执行sorfBind时的调用位置绑定的this var fn = this; var arg1 = [].slice.call(arguments, 1); var bound = function() { //这个this:执行bound时绑定的this return fn.apply( //arg1.concat.call(arg1, arguments 做用是整合两次执行传入的参数 //(!this || this === (window || global)) 猜想是为了在严格模式下也试用 (!this || this === (window || global)) ? obj : this, arg1.concat.call(arg1, arguments) //arguments是bound函数执行时参数的参数, ); }; bound.prototype = Object.create(fn.prototype); return bound; } } function f() { console.log("name: " + this.name); } var obj = {name : "obj"}, obj1 = {name : "obj1"}, obj2 = {name : "obj2"}; var fObj = f.sorfBind(obj); fObj(); //name : obj obj1.foo = f.sorfBind(obj); obj1.foo(); //name : obj1 fObj.call(obj2); //name : obj2 setTimeout(obj1.foo, 10); //name : obj
初次绑定this后,箭头函数内部的this就固定了
function f() { return (b) => { console.log(this.a); } } var obj = {a : 1}; var obj1 = {a : 2}; var fn = f.call(obj); fn(); //1 fn.call(obj1); //1
与第一点相似,初次绑定后内部this固定了也就有了继承
function f() { setTimeout(() => { console.log(this.a); },10); } var obj = {a : 1}; f.call(obj); //1
arguments.callee 也有改变this指向
function f() { console.log(this); if(this ==window) { arguments.callee(); } } f(); //window对象 //arguments对象