没有this时,需要传入上下文获取name,在多个上下文时,代码变得繁杂重复
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identify (context);
console.log(greeting);
}
identify(you); // READER
speak(me); //hello, I'm KYLE
有了this后,代码复用性增加,且更加简洁
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identify.call(this);
console.log(greeting);
}
this
是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。this
绑定方式包括默认绑定、隐式绑定、显式绑定(硬绑定)、new
操作符绑定。
// 例1 默认绑定
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2 ,foo在全局作用域调用,this指向全局对象,在全局对象上找a
// 例2 默认绑定
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2 ,foo的上下文对象变为obj,其this指向obj
// 例3 隐式绑定
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42 ,foo的上下文对象是obj2,其this指向obj2
// 例4 隐式绑定
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global" ,bar引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,上下文对象是全局对象
// 例5 隐式绑定
function foo() {
console.log(this.a);
}
function doFoo(fn) {
// fn其实引用的是foo
fn(); // <-- 调用位置!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a是全局对象的属性
doFoo(obj.foo); // "oops, global",同理,this是在调用时确认,此时真正调用的位置this指向外部全局对象。
// 例6 隐式绑定
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a是全局对象的属性
setTimeout(obj.foo, 100); // "oops, global" 内置函数setTimeout相当于包了一层函数调用
// setTimeout的实现类似下面的伪代码
function setTimeout(fn, delay) {
// 等待delay毫秒
fn(); // <-- 调用位置!
}
// 例7 通过call改变this指向,硬绑定
function foo() {
console.log(this.a);
}
var obj = {
a:2
};
foo.call(obj); // 2
// 例8 通过call绑定this后,不能再改变this的指向(apply同理),称之为硬绑定
function foo() {
console.log(this.a);
}
var obj = {
a:2
};
var bar = function() {
foo.call(obj);
};
bar(); // 2
setTimeout(bar, 100); // 2
// 硬绑定的bar不可能再修改它的this,实际上是foo在调用时被分配了上下文(this),因此不再向外找this,外层的bar被指定任何上下文不对其产生影响
bar.call(window); // 2
// 例9 硬绑定典型应用场景——包裹函数
function foo(something) {
console.log(this.a, something);
return this.a + something;
}
var obj = {
a:2
};
var bar = function() {
return foo.apply (obj, arguments);
};
var b = bar (3); // 2 3,对于任何入参,都将其与对象obj的属性进行协同操作,并按设定的规则返回值
console.log(b); // 5
// 例10 例9中的obj也可以抽象为参数
function foo(something) {
console.log(this.a, something);
return this.a + something;
}
// 简单的辅助绑定函数
function bind(fn, obj) {
return function() {
return fn.apply(obj, arguments);
};
}
var obj = {
a:2
};
var bar = bind(foo, obj);
var b = bar(3); // 2 3
console.log(b); // 5
// 例11 例10中的操作在JavaScript已经被封装为内置函数bind
function foo(something) {
console.log(this.a, something);
return this.a + something;
}
var obj = {
a:2
};
var bar = foo.bind(obj); // 将foo函数中的this绑定为obj
var b = bar(3); // 2 3
console.log(b); // 5
// 例11 内置函数的上下文参数
function foo(el) {
console.log(el, this.id);
}
var obj = {
id: "awesome"
};
// 调用foo(..)时把this绑定到obj
[1, 2, 3].forEach(foo, obj);// forEach传入的第二个参数就是上下文
// 1 awesome 2 awesome 3 awesome
// 例12 new 绑定
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2,使用new来调用foo(..)时,我们会构造一个新对象并把它绑定到foo(..)调用中的this上
四种绑定方式存在优先级:new
绑定 > 显式绑定 > 隐式绑定 > 默认绑定
// 例13 显式绑定优先级高于隐式绑定
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call(obj2); // 3
obj2.foo.call(obj1); // 2
// 例14 new绑定优先级高于隐式绑定
function foo(something) {
this.a = something;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo(2);
console.log(obj1.a); // 2
obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3
var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.log(bar.a); // 4
绑定例外场景
// 例15 call将this绑定到null或undefined时,会应用默认绑定
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null); // 2
// 例16 将this绑定到null的应用——函数柯里化
function foo(a, b) {
console.log("a:" + a + ", b:" + b);
}
// 把数组“展开”成参数
foo.apply(null, [2, 3]); // a:2, b:3
// 使用bind(..)进行柯里化
var bar = foo.bind(null, 2);
bar(3); // a:2, b:3
// 例17 使用null来忽略this绑定会产生难以想象的结果,更安全的做法
function foo(a, b) {
console.log("a:" + a + ", b:" + b);
}
// 我们的DMZ空对象,“DMZ”(demilitarized zone,非军事区)对象——它就是一个空的非委托的对象
var ø = Object.create(null);
// 把数组展开成参数
foo.apply(ø, [2, 3]); // a:2, b:3
// 使用bind(..)进行柯里化
var bar = foo.bind(ø, 2);
bar(3); // a:2, b:3
// 例18 间接引用
function foo() {
console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2,赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo()。
// 例19 软绑定,解决使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改this的问题
if (! Function.prototype.softBind) {
Function.prototype.softBind = function(obj) {
var fn = this;
// 捕获所有 curried 参数
var curried = [].slice.call (arguments, 1);
var bound = function() {
return fn.apply(
(! this || this === (window || global)) ?
obj : this,
curried.concat.apply(curried, arguments)
);
};
bound.prototype = Object.create(fn.prototype);
return bound;
};
}
function foo() {
console.log("name: " + this.name);
}
var obj = { name: "obj" },
obj2 = { name: "obj2" },
obj3 = { name: "obj3" };
var fooOBJ = foo.softBind(obj);
fooOBJ(); // name: obj
obj2.foo = foo.softBind(obj);
obj2.foo(); // name: obj2 <---- 看!! !
fooOBJ.call(obj3); // name: obj3 <---- 看!
setTimeout(obj2.foo, 10);
// name: obj <---- 应用了软绑定
箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
// 例20 箭头函数的词法作用域
function foo() {
// 返回一个箭头函数
return (a) => {
//this继承自foo()
console.log(this.a);
};
}
var obj1 = {
a:2
};
var obj2 = {
a:3
};
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是3!
// foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。
// 例21 对比例20
function foo() {
// 返回一个函数
return function(a){
console.log(this.a);
};
}
var obj1 = {
a:2
};
var obj2 = {
a:3
};
var bar = foo.call(obj1);
bar.call(obj2); // 3,上下文被改变
foo.call(obj1).call(obj2); // 3,
// 例22 回调函数中的this
function foo() {
setTimeout(() => {
// 这里的this在词法上继承自foo()
console.log(this.a);
},100);
}
var obj = {
a:2
};
foo.call(obj); // 2
补充说明:例8中的硬绑定第二个及之后的call
不能改变this
指向,为什么例21后面的call
能改变this
指向?
function foo() {
console.log(this.a);
console.log(this); // obj
}
var obj = {
a:2
};
var bar = function() {
foo.call(obj); // foo在此处被调用,指定了上下文为obj,即this指向obj
console.log(this); // window
};
bar(); // bar在全局上被调用,上下文为全局,即this指向window
// 再看例8,bar在全局上下文被调用,bar的this(上下文)就是window;foo的this是obj,foo有自己的this,不再向外找this
function foo() {
console.log(this.a);
}
var obj = {
a:2
};
var bar = function() {
foo.call(obj);
};
bar(); // 2
// 变体,此时foo调用处没找到this,向外找,最后找到window,因此取的是window上a
function foo() {
console.log(this.a);
}
var obj = {
a:2
};
var bar = function() {
foo();
};
a = 11;
bar(); // 11;
// 再看例21,bar在声明时调用了foo,给foo指定了obj1为其上下文,在调用bar时,又给bar指定了obj2为bar的上下文(this),因此bar的this就是obj2,而foo的上下文不再对其产生影响。在例20中箭头函数没有自己的this,只能向外找,最后找到foo中的this作为其上下文。
function foo() {
// 返回一个函数
return function(a){
console.log(this.a);
};
}
var obj1 = {
a:2
};
var obj2 = {
a:3
};
var bar = foo.call(obj1);
bar.call(obj2); // 3,上下文被改变
foo.call(obj1).call(obj2); // 3
标签:function,绑定,bar,JavaScript,关键字,obj2,var,foo,知道
From: https://www.cnblogs.com/lhjc/p/17834344.html