5. JavaScript中的this
先看一段代码
function foo(num) {
console.log("foo:" + num)
this.count++
}
foo.count = 0
var i
for(i=0;i<10;i++) {
if(i > 5){
foo(i)
}
}
console.log(foo.count) // 0
// 谁调用 this指向谁
console.log(this.count) // NaN
// 如果先定义一个var count = 0; console.log(this.count) 是多少?
// 能否用其他方法获取count的值 foo.count call
需要注意的是 this与作用域是不同的概念
function foo() {
var a = 2
this.bar()
}
function bar() {
console.log(this.a)
}
foo() // 报错
你不知道的JavaScript上
this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调
用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包
含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的
其中一个属性,会在函数执行的过程中用到。
规则
- 默认绑定
```javascript function foo () { console.log(this.a) }
var a = 2 foo() // 2
// 如果加了严格模式 “use strict” foo() // TypeError: this is undefined
2. 隐式绑定
```javascript
function foo() {
console.log( this.a )
}
var obj = {
a: 2,
foo,
}
obj.foo() // 2
// 对象属性引用链中只有最顶层或者说最后一层会影响调用位置
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
}
var obj1 = {
a: 2,
obj2: obj2
}
obj1.obj2.foo() // 42
// 隐式丢失
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
}
var bar = obj.foo
var a = "oops, global"
bar() // "oops, global"
- 显示绑定
- 硬绑定
```javascript function foo() { console.log( this.a ); } var obj = { a:2 }; var bar = function() { foo.call( obj ); }; bar(); // 2 setTimeout( bar, 100 ); // 2 // 硬绑定的 bar 不可能再修改它的 this bar.call( window ); // 2
- 硬绑定
// 使用场景 // 1 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 console.log( b ); // 5 // 2 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 // bind function foo(something) { console.log( this.a, something ); return this.a + something; } var obj = { a:2 }; var bar = foo.bind( obj ); var b = bar( 3 ); // 2 3 console.log( b ); // 5
```javascript
// 实现一个bind
Function.prototype._bind = function(ctx = window) {
if(typeof this !== 'function') {
throw new TypeError ( this + 'is not a function')
}
var _this = this,
args = [].slice.call(arguments,1),
emptyFn = function (){},
binded = function () {
var newArgs = [].slice.call(arguments);
_this.apply(this instanceof binded ? this : ctx , newArgs.concat(args))
}
emptyFn.prototype = this.prototype;
binded.prototype = emptyFn.prototype;
return binded;
}
// 实现一个apply
Function.prototype._apply = function (ctx = window,arr){
ctx.fn = this;
if( typeof arr !== 'object' && typeof arr !== 'function' && typeof arr !== 'undefined'){
throw TypeError ('CreateListFromArrayLike called on non-object')
}
if( !arr || Object.prototype.toString.call(arr) !== 'object Array'){
delete ctx.fn;
return ctx.fn()
}
let res = ctx.fn(...arr)
return res;
}
// 实现一个call
Function.prototype._call = function (ctx = window) {
ctx.fn = this;
let args = [...arguments].slice(1);
let res = ctx.fn(...args);
delete ctx.fn;
return res;
}
- API调用的“上下文”
function foo(el) { console.log( el, this.id ); } var obj = { id: "awesome" }; // 调用 foo(..) 时把 this 绑定到 obj [1, 2, 3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome
- new绑定
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
// 实现一个new
// 规则
/*
1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行 [[ 原型 ]] 连接。
3. 这个新对象会绑定到函数调用的 this。
4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
*/
function _new() {
var constructor = [].shift.call(arguments),
_this = {};
_this.__proto__ = constructor.prototype
var res = constructor.apply(_this,arguments)
return typeof res === 'object' ? res : _this
}
- 箭头函数
根据当前的词法作用域来决定
this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。
优先级
// 显式与隐式绑定
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
// 隐式绑定与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
// 显式绑定与new
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); // 2
var baz = new bar(3);
console.log( obj1.a ); // 2
console.log( baz.a ); // 3
面试题
// 1
var fullName = 'language'
var obj = {
fullName: 'javascript',
prop: {
getFullName: function () {
return this.fullName
}
}
}
console.log(obj.prop.getFullName())
var test = obj.prop.getFullName
console.log(test())
// 2 *
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
// 3
const a = {
b: 2,
foo: function () { console.log(this.b) }
}
function b(foo) {
// 输出什么?
foo()
}
b(a.foo)