Js中的this的使用规则

112 阅读5分钟

为什么要有this

this 让函数可以自动引用合适的上下文对象

使用它可以减少传参,让代码更加优雅

function identify(context) {
  return context.name, toUpperCase()//转化为大写
}
function speak(context) {
  var gretting = 'Hello,I am' + indentify(context)
  console.log(gretting)
}

var me = {
  name: 'Tom'
}

speak(me)
function identify() {
  return this.name, toUpperCase()//转化为大写
}
function speak() {
  var gretting = 'Hello,I am' + indentify.call(this)//此处使用了显性绑定,后面会讲
  console.log(gretting)
}

var me = {
  name: 'Tom'
}

speak.call(me)

this

  1. this 是一个代词,在js中永远代表某一个域,且this只存在域中才有意义(全局作用域,函数作用域,块级作用域)
  2. this在浏览器全局下指向的是window

在编译软件中代表的是node的全局global

image-1.png

在浏览器中是window image.png

this的指向:

  1. 默认绑定: 规则: 函数独立调用时,触发,this指向window

  2. 隐式绑定: 规则: 当一个函数被引用(函数被某一个对象拥有且触发且调用),this指向该上下文对象

  3. 隐式丢失: 规则: 当函数的应用有一连串上下文对象,this指向最近的上下文对象

  4. 显式绑定: 规则: call apply bind 显示的将函数的 this 绑定到一个对象上

  5. new绑定: 规则:this指向实例对象 当一个函数存在return且返回的是一个引用类型的数据,new返回引用类型

默认绑定

函数独立调用时,触发,this指向window

function foo() {
  let person = {
    name: "amei",
    age: 18
  }
  console.log(this);//它是foo函数域中的,不一定代表函数体
}
foo();//在全局中调用,this指向全局

function bar() {
  let person = {
    name: "管总",
    age: 18
  }
  foo()//在函数中调用,this还是指向全局
}
bar();

独立调用 函数是什么

独立调用函数是指一个函数以普通的方式直接调用,而不是作为对象的方法调用 方法调用 ,也不是通过 call、apply 或 bind 等方法绑定上下文调用 显式绑定。后面两种方法会讲

隐式绑定

当一个函数被引用(函数被某一个对象拥有且触发且调用),this指向该上下文对象

function foo() {
  console.log(this)
}
const obj = {
  a: 1,
  foo: foo//----------------------这这种方法是方法调用,会让this指向obj这个对象
}
obj.foo()//

方法调用是什么

让一个对象拥有这个函数

隐式绑定丢失

当函数的应用有一连串上下文对象,this指向最近的上下文对象

const obj2 = {
  a: 2,
  obj: obj
}
obj2.obj.foo()-------此处foo指向obj,不指向obj2

显式绑定

call apply bind 显示的将函数的 this 绑定到一个对象上

call,apply,bind是构造函数Function的现式原型(proto)上的一个方法,当函数被创建的时候会把它的显式原型赋给这个函数的隐式原型__proto__(函数也是对象,也会有隐式原型和显式原型),然后函数就可以调用它了。(看不懂去看原型和原型链)

function foo() {
  console.log(this.a)
}
var obj = {
  a: 1
}
foo()

foo.call(obj)

call apply bind他们三个作用差不多,只是传参的方法不一样

function foo(a,b) {
  console.log(this.a+a+b)
}
var obj = {
  a: 1
}
foo()

foo.call(obj,2,3)//---------------call此处传的参数是散的
function foo(a,b) {
  console.log(this.a+a+b)
}
var obj = {
  a: 1
}
foo()

foo.apply(obj,[1,2])//------------apply与call不同,接受参数用数组

function foo(a, b) {
  console.log(this.a + a + b)
}
var obj = {
  a: 1
}
foo()

let bar = foo.bind(obj, 2, 3)//-------与call一样零散接受参数,但返回函数体
bar()

let bar = foo.bind(obj, 2)//----------与call一样零散接受参数,并返回函数体
bar(1)//没有采集完参数会在函数体里

new

  1. 创建一个空对象obj
  2. 将构造函数里的this指向obj
  3. 正常运行构造函数里的代码
  4. obj的隐式原型等于构造函数的显示原型
  5. 返回obj

类似于:

function Person() {
  // let obj = {
  //   name: '阿炜',
  //   age: 18
  // }
  // Person.call(obj)
  // obj.__proto__=Person.prototype
  // return Person() instanceof Object? Person():obj----此处如果函数返回引用对象,则不返回值

  this.name = '阿炜'
  this.age = 18
  return 123
}
let p = new Person()
console.log(p)//不会return基础类型,会返回应勇类型

箭头函数

箭头函数没有this,写在了箭头函数中的this也是它外层非箭头函数的

function foo(){
  let bar =function(){
    let baz =()=>{
      console.log(this);
    }
    baz()----------------------此处的baz没有this,它的this会默认指向外层的非箭头函数,所有指向bar
  }
}
foo()

箭头函数不能作为构造函数使用

//错误代码
let Foo = () => {
  this.name = '廖总'
}

let foo = new Foo()

显式绑定call的原理和代码实现

call的原理

让函数被实例对象拥有,然后执行函数,把函数的this绑定到实例对象上(使用了this的隐式调用方法) 然后删除该函数

function foo(x) {
  console.log(this.a, x);
  return 123
}
const obj = {
  a: 1
  //b:foo-----------------让这个实例对象拥有这个函数
}

foo.call(obj, 2)
Function.prototype.myCall=function(...args){//不会的回去看看ES6的传参解决办法
//步骤:
//1,将foo引用到obj上
//2,调用foo
//3,移除obj上的foo
const context=args[0]
const arg=args.slice(1)//这是数组上的方法切成[2,3,4]
context.fn=this//函数被foo引用,this指向foo
const res=context.fn(...arg)//这是Es6里的方法...arg会把arg解构,变成2,3,4此处调用该函数。|| 让它执行,如果该函数返回值,接受它的return值,再去把它的return值返回
delete context.fn
return res//返回函数的值,要不然会把函数返回值的功能搞没
}
foo.myCall(obj,2)

结语

this面试官经常问,this非常重要

然后写文章容易,给我个赞谢谢!!!!!!