**this**
是 this 那条语句所在的执行上下文,在非严格模式下,全局指向**window**
,在函数中则取决于函数的调用位置(即函数被谁调用了)
浏览器(runtime)
非严格模式
- this的四种绑定规则
/**
- 就是function a,挂载在window上
- 所以当执行a(),可以理解成window.a()
*/
console.log(window.a);
```javascript // 案例三 let obj = {} function a() { function b() { function c() { console.log(this) } c() } b.apply(obj) } a(); // window /**```javascript
// 案例二
function a() {
function b() {
function c() { console.log(this) }
c()
}
b()
}
a(); // window
- 这说明c的this并不受所在函数b的影响
- 仅仅是因为直接c(),这样调用c函数,即使用了默认绑定的方式
*/
```
隐式绑定
```javascript // 案例一 let obj = { a: function() {
} } obj.a(); // obj /**console.log(this)
- 很明显,a被obj调用,所以函数a里面的this指向obj
*/
let fn = obj.a fn(); // window /**
- obj.a赋给了fn。调用fn的时候,前面没有xxx.fn,而是直接fn(),所以采用了默认绑定
*/
} } }console.log(this)
let fn = obj.a(); fn(); // window
<a name="j3MHy"></a>
### 显示绑定
- call、apply、bind的区别
- `fn.call( <this> ,p1,p2...)`
- `fn.apply( <this> ,[p1,p2...])`
- `fn.bind( <this> )(p1,p2...)`
- 注意:如果把`null`或者`undefined`作为 this 的绑定对象传入`call`、`apply`或者`bind`,这些值在调用时会被忽略,实际应用的是默认绑定规则。
- 注意:如果传入的是 `值类型` 而不是 `引用类型` 的话,那么将会把`值类型`隐式转换为对应的 `对象形式`(也就是 `new String()`、`new Number()`之类的)
```javascript
// 案例一
let obj = {
name: 'kons'
}
function a() {
console.log(this.name);
}
a.call(obj); // kons
a.apply(obj); // kons
a.bind(obj)(); // kons
// 案例二
// 理解bind的实现,简陋版
Function.prototype.mybind = function (obj) {
let __this = this; // 获取函数实例
return function (...rest) {
__this.apply(obj, rest); // 硬绑定
}
}
// 知道了这个之后,就知道下面这个,为什么是window,而不是obj
let obj = {
a: function () {
console.log(this)
}
}
let fn = obj.a
fn.bind(null).call(obj); // window
// 手写bind函数
Function.prototype.mybind = function (objThis, ...params) {
const thisFn = this; // 保存当前调用的函数
let funcForBind = function (...otherParams) {
// 如果是new出来的,this会指向新new出来的对象。如果不是,this是指向window,改为指向objThis
const isNew = this instanceof funcForBind;
const thisArg = isNew ? this : objThis;
return thisFn.call(thisArg, ...params, ...otherParams);
}
// 克隆一下
funcForBind.prototype = Object.create(thisFn.prototype);
return funcForBind;
}
new绑定
- 定义JS的“构造函数”:在JS中,构造函数只是一些使用
new
操作符时被调用的函数。它们只是被new
操作符调用的普通函数而已。- 包括内置对象函数,Number、String等等,也只是普通函数
- 实际上并不存在所谓的“构造函数”,只有对函数的“构造调用”
new
来调用函数、或者说发生构造函数调用时,会自动执行下面的五步- 1.创建一个新的空对象
{}
- 2.将构造函数的
prototype
属性赋值给新对象的__proto__
- 3.将构造函数的this指向新对象
- 4.执行构造函数的代码
- 5.如果函数没有返回其他
引用类型
,那么new表达式中的函数调用会自动返回这个新对象 ```javascript // 案例一 //在声明函数的时候,会自动创建一个prototype属性,我们管他叫函数原型对象,一般用来存放实例公用的方法 function CreateCat(name){ this.name = name; }
- 1.创建一个新的空对象
CreateCat.prototype.eat = function(something){ console.log(this.name + ‘ eat ‘ + something); } /**
- CreateCat.prototype = new Object() —> {
- proto: Object.prototype,
- constructor: CreateCat(){…},
- eat: function(something){…}
- } */
var catA = new CreateCat(‘xiaoA’); /**
- catA = {
- proto: CreateCat.prototype,
- name: ‘xiaoA’
- }
*/
const one = new Test();
console.log(one); // {name: ‘Test’, count: 2}
```javascript
// 案例三
function Test() {
this.name = 'Test';
this.count = 2;
return [456]; // 换成其他引用类型就会改变,如{go:4}、function(){}、new Number(456)
}
const one = new Test();
console.log(one); // [456]
四种绑定的优先级
- new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
绑定的例外
软绑定
- 作用:解决由于硬绑定,无法使用 隐式绑定 或者 显示绑定 来修改this
// 软绑定的实现
Function.prototype.softBind = function(objThis,...params) {
let thisFn = this;
let bound = function(...otherParams){
let context = (!this || this === (window || global)) ? objThis: this;
return thisFn.call(context, ...params, ...otherParams);
}
bound.prototype = Object.create(thisFn.prototype);
return bound;
}
// 使用方法
let obj = {};
function a() {
console.log(this);
}
a.bind(null).apply(obj); // window
a.softBind(null).apply(obj); // obj
- 作用:解决由于硬绑定,无法使用 隐式绑定 或者 显示绑定 来修改this
箭头函数
- 函数体内的this对象,继承的是外层代码块的this
- 函数没有prototype原型
- 不可以作为构造函数。new了会报错
- 没有
arguments
- 箭头函数没有自己的this,所以不能用call()、apply()、bind()这些方法去改变this的指向