JS函数的this指向

JS函数的this指向

this作用

JavaScript中的this和常见的面向对象语言中的this区别:

  • 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。
  • 也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。
  • 但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义
JavaScript 复制代码
// 没有this,编写代码不方便
var obj1 = {
	name :"why",
	eating: function() {
		console.log(obj1.name,+ "在吃东西")
	},
	running: function() {
		console. log(obj1.name + "在跑步")
	}
}

var info= {
	name :"why",
	eating: function() {
		console.log(info.name + "在吃东西")
	},
	running: function() {
		console. log(info.name + "在跑步")
	}
}

this指向

全局作用域

  • 浏览器: window (global0bject)
  • Node环境:{} (把每个文件当成module -> 加载->编译->放到一个函数compiledWrapper ->执行这个函数.call({}))

this通常在函数中使用

动态绑定:this在函数执行时才会确定

函数执行上下文(函数的调用栈、AO对象、this

  • 函数在调用时,JavaScript会默认给this绑定一个值;
  • this的绑定和定义的位置(编写的位置)没有关系;
  • this的绑定和调用方式以及调用的位置有关系;
  • this是在运行时被绑定的;
JavaScript 复制代码
// this指向什么,跟函数所处的位置是没有关系的
// 跟函数被调用的方式是有关系
function foo() {
	console.log(this)
}

// 1. 直接调用这个函数
foo()

// 2. 创建一个对象,对象中的函数指向foo
var obj = {
	name: 'why',
	foo:foo
}

obj. foo()

// 3. call/apply调用
foo.apply("abc")

this绑定规则

  1. 默认绑定:独立函数调用

没有调用主题

  1. 隐式绑定:object.fn()

前提:

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性);
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
  • 正是通过这个引用,间接的将this绑定到了这个对象上;

object对象会被js引擎绑定到fn函数的中this里面

在其调用位置中,是通过某个对象发起的函数调用。

  1. 显示绑定:call、apply、bind

call、apply

JavaScript所有的函数都可以使用call和apply方法(这个和Prototype有关)。

第一个参数都要求是一个对象(给this准备),后面的参数,apply为数组,call为参数列表;

在调用这个函数时,会将this绑定到这个传入的对象上

JavaScript 复制代码
    function sum(num1, num2, num3) {
      console.log(num1 + num2 + num3, this)
    }

    sum.call("call", 20, 30, 40)
    sum.apply("apply", [20, 30, 40])

foo直接调用和 call/apply 调用的不同在于this绑定的不同

foo直接调用指向的是全局对象(window);call/apply是可以指定this的绑定对象

bind

JavaScript 复制代码
  function foo() {
    console.log(this)
  }

  // 默认绑定和显示绑定bind冲突: 优先级(显示绑定)
  var newFoo = foo.bind("aaa")

  newFoo()
  1. new绑定
  • 创建一个全新的对象;
  • 这个新对象会被执行prototype连接;
  • 这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
  • 如果函数没有返回其他对象,表达式会返回这个新对象;
JavaScript 复制代码
    // 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
    // this = 创建出来的对象
    // 这个绑定过程就是new 绑定

    function Person(name, age) {
      this.name = name
      this.age = age
    }

    var p1 = new Person("why", 18)
    console.log(p1.name, p1.age)  // this->Peson {}

    var p2 = new Person("kobe", 30)
    console.log(p2.name, p2.age)

this其他补充

JavaScript 复制代码
setTimeout(function() {
  console.log(this) // window,内部是**独立函数调用**
}, 2000) // 默认绑定
JavaScript 复制代码
const boxDiv = document.querySelector('.box')
boxDiv.onclick = function() {
  console.log(this) // boxDiv元素
} // 隐式绑定

boxDiv.addEventListener('click', function() {
  console.log(this) // boxDiv元素
}) // 显示绑定
JavaScript 复制代码
// 3.数组.forEach/map/filter/find,可以传两个参数(函数、this指向)
var names = ["abc", "cba", "nba"]
names.forEach(function(item) {
  console.log(item, this)
}) // window
names.map(function(item) {
  console.log(item, this)
}, "cba")  // "cba"

规则优先级

1.默认规则的优先级最低

2.显示绑定优先级高于隐式绑定

JavaScript 复制代码
// 1.call/apply的显示绑定高于隐式绑定
var obj = {
  name: "obj",
  foo: function() {
    console.log(this)
  }
}

obj.foo.apply('abc') // 'abc'
obj.foo.call('abc')  // 'abc'

// 2.bind的优先级高于隐式绑定
function foo() {
  console.log(this)
}

var obj = {
  name: "obj",
  foo: foo.bind("aaa")
}

obj.foo() // "aaa"

3.new绑定优先级高于隐式绑定

JavaScript 复制代码
var obj = {
  name: "obj",
  foo: function() {
    console.log(this)
  }
}

// new的优先级高于隐式绑定
var f = new obj.foo() // foo {}

4.new绑定优先级高于bind

JavaScript 复制代码
// 结论: new关键字不能和apply/call一起来使用

// new的优先级高于bind
function foo() {
  console.log(this)
}

var bar = foo.bind("aaa")

var obj = new bar() // foo {}

// new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

this规则之外

  1. 忽略显示绑定
JavaScript 复制代码
function foo() {
  console.log(this)
}

foo.apply("abc") // "abc"
foo.apply({}) // {}

// apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
foo.apply(null)  // window 
foo.apply(undefined) // window

var bar = foo.bind(null)
bar() // window
  1. 间接函数引用
JavaScript 复制代码
// 争论: 代码规范 ;

var obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this)
  }
}

var obj2 = {
  name: "obj2"
}; // 不加";"词法分析会认为代码还未结束,与下面的(obj2.bar = obj1.foo)()一起

// obj2.bar = obj1.foo
// obj2.bar()  //obj2

(obj2.bar = obj1.foo)()  //独立函数调用
  1. ES6箭头函数(arrow function)
JavaScript 复制代码
// 1. 编写箭头函数
// 1> (): 参数
// 2> =>: 箭头
// 3> {}: 函数的执行体
var foo = (num1, num2, num3) => {
  console.log(num1, num2, num3)
}

function bar(num1, num2, num3) {
}

// 高阶函数在使用时, 也可以传入箭头函数
var nums = [10, 20, 45, 78]
nums.forEach((item, index, arr) => {})

// 箭头函数有一些常见的简写:
// 简写一: 如果参数只有一个, ()可以省略
nums.forEach(item => {
  console.log(item)
})

// 简写二: 如果函数执行体只有一行代码, 那么{}也可以省略
// 强调: 并且它会默认将这行代码的执行结果作为返回值,不用return
nums.forEach(item => console.log(item))
var newNums = nums.filter(item => item % 2 === 0)
console.log(newNums) // [10,20,78]

// filter/map/reduce
var result = nums.filter(item => item % 2 === 0)
                 .map(item => item * 100)
                 .reduce((preValue, item) => preValue + item)
console.log(result) // 10800

// 简写三: 如果一个箭头函数, 只有一行代码, 并且返回一个对象, 这个时候如何编写简写
// var bar = () => {
//   return { name: "why", age: 18 }
// }

var bar = () => ({ name: "why", age: 18 })

箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this。

JavaScript 复制代码
// 1.测试箭头函数中this指向
// var name = "why"

// var foo = () => {
//   console.log(this)
// }

// foo() //window
// var obj = {foo: foo}
// obj.foo() //window 
// foo.call("abc") // window

// 2.应用场景
var obj = {
  data: [],
  getData: function() {
    // 发送网络请求, 将结果放到上面data属性中
    // 在箭头函数之前的解决方案
    // var _this = this
    // setTimeout(function() {
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // 箭头函数之后
    // 箭头函数没有this,就会向上层作用域找,上层function隐式绑定obj
    setTimeout(() => {
      var result = ["abc", "cba", "nba"]
      this.data = result
    }, 2000);
  }
}

obj.getData()
相关推荐
只会安静敲代码的 小周13 分钟前
uniapp上传图片时(可选微信头像、相册、拍照)
前端·微信·uni-app
kovlistudio30 分钟前
红宝书第四十六讲:Node.js基础与API设计解析
前端·javascript·node.js
陈哥聊测试31 分钟前
这款自研底层框架,你说不定已经用上了
前端·后端·开源
m0_zj37 分钟前
41.[前端开发-JavaScript高级]Day06-原型关系图-ES6类的使用-ES6转ES5
开发语言·javascript·es6
蘑菇头爱平底锅1 小时前
数字孪生-DTS-孪创城市-低空范围
前端·javascript·数据可视化
KenXu1 小时前
Module Federation v0.12新特征详解
前端
avocado_green1 小时前
【学习笔记】从mobx迁移到redux时的概念映射
javascript
三原1 小时前
前端微应用-乾坤(qiankun)原理分析-沙箱隔离(css)
前端·架构·前端框架
琦遇1 小时前
Vue3使用AntvG6写拓扑图,可添加修改删除节点和边
前端·javascript·vue.js
Luckyfif1 小时前
🗣️面试官:有一个比较经典的 case 也想探讨一下 「页面白屏如何排查?」❤️✨
前端·面试·开源