1、this的指向
函数中的this总指向调用它的对象。
2、this的绑定形式
2.1、this的默认绑定
当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象。
凡是函数作为独立函数调用,无论它的位置在哪里,它的行为表现,都和直接在全局环境中调用无异。
如:
var obj = {
fire: function () {
function innerFire() {
console.log(this === window)
}
innerFire(); // 独立函数调用
}
}
obj.fire(); //输出 true
2.2、this的隐式绑定
当函数被一个对象“包含”的时候,我们称函数的this被隐式绑定到这个对象里面了,这时候,通过this可以直接访问所绑定的对象里面的其他属性。
fire函数并不会因为它被定义在obj对象的内部和外部而有任何区别,如下:
// 我是第一段代码
function fire () {
console.log(this.a)
}
var obj = {
a: 1,
fire: fire
}
obj.fire(); // 输出1
// 我是第二段代码
var obj = {
a: 1,
fire: function () {
console.log(this.a)
}
}
obj.fire(); // 输出1
注意点:
-
this是动态绑定的,或者说是在代码运行期绑定而不是在书写期; 基于this动态绑定的特点,写在对象内部,作为对象属性的函数,对于这个对象来说是独立的。(函数并不被这个外部对象所“完全拥有”)
-
函数于对象的独立性, this的传递丢失问题;如函数赋值的过程:
var obj = {
a: 1, // a是定义在对象obj中的属性 1
fire: function () {
console.log(this.a)
}
}
var a = 2; // a是定义在全局环境中的变量 2
var fireInGrobal = obj.fire;
fireInGrobal(); // 输出 2
3、在一串对象属性链中,this绑定的是最内层的对象;
var obj = {
a: 1,
obj2: {
a: 2,
obj3: {
a:3,
getA: function () {
console.log(this.a)
}
}
}
}
obj.obj2.obj3.getA(); // 输出3
2.3、this的显示绑定(call\apply\bind)
关于this的传递丢失问题,call()、apply()、bind() 都是用来重定义 this 这个对象的!
call的基本使用方式:fn.call(object,'args1','args2')
fn是你调用的函数,object参数是你希望函数的this所绑定的对象。
-
即刻调用这个函数(fn)
-
调用这个函数的时候函数的this指向object对象
apply的基本使用方式:fn.apply(object,['args1','args2'])
- 即刻调用这个函数(fn)
bind的基本使用方式:
fn.bind(object,'args1','args2')或者fn.bind(object,['args1','args2'])
- bind不执行函数,只返回一个可供执行的函数;
var obj = {
name:'duizhang',
age:'22',
adress:'1128'
}
function print(){
console.log(this); // 打印 this 的指向
console.log(arguments); // 打印传递的参数
}
// 通过 call 改变 this 指向
print.call(obj,1,2,3);
// 通过 apply 改变 this 指向
print.apply(obj,[1,2,3]);
// 通过 bind 改变 this 的指向
let fn = print.bind(obj,1,2,3);
fn();
2.4、new绑定
执行new操作的时候,将创建一个新的对象,并且将构造函数的this指向所创建的新对象 ;
function foo (a) {
this.a = a;
}
var a1 = new foo (1);
var a2 = new foo (2);
var a3 = new foo (3);
var a4 = new foo (4);
console.log(a1.a); // 输出1
console.log(a2.a); // 输出2
console.log(a3.a); // 输出3
console.log(a4.a); // 输出4
3、箭头函数中的this
由于箭头函数没有单独的 this 值。箭头函数的 this 与声明所在的上下文相同。也就是说调用箭头 函数的时候,不会隐士的调用 this 参数,而是从定义时的函数继承上下文。
const obj = {
a:()=>{
console.log(this);
}
} /
/ 对象调用箭头函数
obj.a(); // window
4、call、apply、bind 实现
4.1、手写call
- 首先 context 为可选参数,如果不传的话默认上下文为 window ;
- 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数;
- 因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来;
- 然后调用函数并将对象上的函数删除。
// this 为调用的函数
// context 是参数对象
Function.prototype.myCall = function(context){
console.log("this",this);//此时this指向调用函数 person
// 判断调用者是否为函数
if(typeof this !== 'function'){
throw new TypeError('Error')
}
// 不传参默认为 window
context = context || window
// 通过为context新增fn属性,并将this赋值fn函数
context.fn = this
// 将 arguments 转化为数组,将 call 的传参提取出来 [...arguments]
const args = Array.from(arguments).slice(1)
// 传参调用函数,此时this指向context
const result = context.fn(...args)//数组解构...args
// 删除函数
delete context.fn
// 返回执行结果
return result;
}
// 普通函数
function person(age){
console.log(this.name+" "+age);
}
// 自定义对象
var obj = {
name:'yyqx'
}
// 调用函数的 call 方法
person.myCall(obj,18,2,3)
4.2、手写apply
- 首先 context 为可选参数,如果不传的话默认上下文为 window
- 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数
- 因为 apply 传参是数组传参,所以取得数组,将其剥离为顺序参数进行函数调用
- 然后调用函数并将对象上的函数删除
Function.prototype.myApply = function(context){
// 判断调用者是否为函数
if(typeof this !== 'function'){
throw new TypeError('Error')
}
// 不传参默认为 window
context = context || window
// 新增 fn 属性,将值设置为需要调用的函数
context.fn = this
// 返回执行结果
let result;
// 判断是否有参数传入
if(arguments[1]){
result = context.fn(...arguments[1])
}else{
result = context.fn()
}
// 删除函数
delete context.fn
// 返回执行结果
return result;
}
// 普通函数
function print(age,age2,age3){
console.log(this.name+" "+ age + " "+ age2+" "+age3);
}
// 自定义对象
var obj = {
name:'小鹿'
}
// 调用函数的 call 方法
print.myApply(obj,[1,2,3])
4.3、手写bind
- 判断调用者是否为函数。
- 截取参数,注意:这里有两种形式传参。
- 返回一个函数,判断外部哪种方式调用了该函数(new | 直接调用)
Function.prototype.myBind = function (context) {
// 判断调用者是否为函数
if(typeof this !== 'function'){
throw new TypeError('Error')
}
// 截取传递的参数
const args = Array.from(arguments).slice(1)
// _this 指向调用的函数
const _this = this;
// 返回一个函数
return function F(){
// 因为返回了一个函数,我们可以 new F(),所以需要判断
// 对于 new 的情况来说,不会被任何方式改变 this
if(this instanceof F){
return new _this(...args,...arguments)
}else{
return _this.apply(context,args.concat(...arguments))
}
}
}
/
/ 普通函数
function print(){
// new 的方式调用 bind 参数输出换做 [...arguments]
console.log(this.name);
}
// 自定义对象
var obj = {
name:'小鹿'
}
// 调用函数的方法
let F = print.myBind(obj,1,2,3);
F()
// 返回对象的方式
let obj1 = new F();
console.log(obj1);
5、自执行函数中的this
自执行函数的this都指向window。
(function(){
console.log(this.foo);//this指向的是window
})();
原文参考: 1、mp.weixin.qq.com/s/QAXswIi-y…