一、this的作用
developer.mozilla.org/zh-CN/docs/…
1、与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。
2、在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也不一样。
一、this的作用
developer.mozilla.org/zh-CN/docs/…
1、与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。
2、在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。
let obj = {
name: "哈哈哈",
eating: function () {
/**
* 这块不使用this,也是可以的
* 但是如果 你 obj 改变了,下面就要改变,这样会很不方便(在开发过程中,要切记不要低代码开发)
*/
console.log(obj.name + "吃东西")
},
running: function () {
console.log(this.name + "运动")
},
studying: function () {
console.log(this.name + "学习")
}
}
obj.eating() // 哈哈哈吃东西
obj.running() // 哈哈哈运动
obj.studying() // 哈哈哈学习
可查看下面这个案例(引用于MDN):
二、this的作用域
1、在全局作用域下
注:在全局作用域下,this指向window
console.log(this)
console.log(window)
2、在node环境下,this的指向
1)在node下,指向空的对象
2)在node下执行(其实源码里面是call来实现的)
3、this在函数中的使用
注:this指向什么,跟函数所处的位置是没有关系的,跟函数 被调用的方式是有关系的;他是在运行时被绑定的
function fun(){
console.log(this)
}
/**
* 下面提供了三种调用方式
*/
fun()
let obj = {
name:"哈哈",
/**
* foo现在指向上面的函数
*/
foo: fun
}
obj.foo()
fun.apply("啊啊啊啊")
三、this的绑定规则
注:this的指向,与调用的位置(或方式)有关
1、默认绑定
注:独立函数调用,也就是在window下 函数名/变量名() 这样子去运行的;未加任何绑定(或者 .XXX() 去进行运行的绑定)
/**
* 独立函数调用,这个时候 指向window
* 下面的案例,一块不太好看,你可以挨个去运行,查看this的指向
*/
// 案例1
function fun1() {
console.log("fun1", this)
}
fun1() // Window
/**
* 下面都是 独立被调用
*/
// 案例2
function fun2() {
console.log("fun2", this)
}
function fun3() {
console.log("fun3", this)
fun2()
}
function fun4() {
console.log("fun4", this)
fun3()
}
fun4()
// 案例3 *
let obj = {
name: "哈哈哈",
fun5: function () {
console.log("fun5", this)
}
}
// obj.fun5() // 这个有调用主题(他不是 独立的,这个时候,它指向obj)
const fun6 = obj.fun5
fun6() // 指向window
// 案例4
function fun7(){
console.log("fun7", this)
}
let obj2 = {
name: "哈哈哈",
fun: fun7
}
// obj.fun5() // 这个有调用主题(他不是 独立的,这个时候,它指向obj)
const fun8 = obj2.fun
fun8() // 指向window
// 案例5
function fun9(){
function bar(){
console.log("fun9", this)
}
return bar
}
const fun10 = fun9()
fun10()
2、隐式绑定
注:通过某个对象,进行的函数调用
// case1
function fun1(){
console.log(this)
}
let obj1 = {
age:222,
foo: fun1
}
obj1.foo() // obj1
let bar = obj1.foo
bar() // window
// case2
let obj2 = {
age:22,
foo: function () {
console.log(this, this.age + obj2.age)
}
}
obj2.foo() // {age: 22, foo: ƒ} 44
// case3
let obj3 = {
name: "obj3",
foo: function () {
console.log(this)
}
}
let obj4 = {
name: "obj4",
// 对象中的属性,指向函数
foo: obj3.foo
}
obj4.foo() // {name: 'obj4', foo: ƒ}
3、显示绑定
call、apply、bind 改变函数体内的this指向
语法:函数.call()、函数.apply()、函数.bind()
1)call
① call立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.call(obj) // 立即执行 {age: 22, name: '小明'} '小明'
fun() // Window ""
② call有多个参数时需要罗列(也就是按个书写)
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.call(obj, 222, "boy") // {name: '小明', age: 222, gender: 'boy'} '小明'
2)apply
① apply立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.apply(obj) // 立即执行
fun()
② apply的第二个参数是数组
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.apply(obj, [ 222, "boy" ])
3)bind
① bind不立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.bind(obj) // 不立即执行(他返回出来 是一个函数)
fun.bind(obj)() // 立即执行要jia 括号
fun()
② bind有多个参数时需要罗列(也就是按个书写)
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.bind(obj, 222, "boy")()
function fun(){
console.log(this)
}
/**
* 1、bind的返回值 是一个函数
* 2、bind是函数的方法
* 3、传入的参数,就是绑定的this
*/
let newFun = fun.bind("传入的参数(也就是绑定this),就是绑定的this")
newFun() // String {'传入的参数(也就是绑定this),就是绑定的this'}
4)call、apply、bind使用场景
let arr = [ 1, 2, 4, 6, 8, 11, 134, 0 ]
// console.log(Math.max(arr)) // NaN
/**
* 这块不涉及this的绑定,所以穿空
* 这块只能时apply,因为apply的第二个参数 是数组
* Math.XXX 的apply只能写在类似于下面的这个位置
*/
console.log(Math.max.apply(null ,arr)) // 134
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<input type="button" value="按钮">
<br>
<h2>内容</h2>
</body>
</html>
<script>
const but = document.querySelector("input")
const content = document.querySelector("h2")
/**
* 这块就要使用 bind ,因为 你不想让他自动触发
*/
but.onclick = function (){
console.log(this) // 指向h2
}.bind(content)
</script>
4、自己实现call、apply、bind方法
1)call的实现
let fun = {
getName: function () {
return this.name
}
}
let obj = {
name: "小明"
}
/**
* 1、手写call,要使用重新的这个 方法挂载到Function的属性上
* 2、这个里面的this时谁?
* 谁调用的他,这个this 就指向谁,这块的话,是getName调用的,所以 this就是 getName这个函数
*/
Function.prototype.myCall = function (context) {
// console.log(arguments)
// console.log(this)
/**
* 判断是否为函数,不是函数的时候,抛出错误
*/
if (Object.prototype.toString.call(this).match(/\[object (.*?)\]/)[1].toLowerCase() !== 'function') {
throw new Error("错误")
}
context = context || window
let args = [ ...arguments ].slice(1)
// console.log(args)
/**
* 在这 把 this赋值给context这个参数
* 但是要假设 context 上有一个方法
*/
context.fn = this
let result = context.fn(...args)
// console.log(result)
return result
}
console.log(fun.getName.myCall(obj)) // 小明
2)apply的实现
let fun = {
getName: function (a, b) {
console.log(a + b)
return this.name
}
}
let obj = {
name: "小明"
}
/**
* 1、手写apply,要使用重新的这个 方法挂载到Function的属性上
* 2、这个里面的this时谁?
* 谁调用的他,这个this 就指向谁,这块的话,是getName调用的,所以 this就是 getName这个函数
*/
Function.prototype.myAplly = function (context) {
// console.log(arguments)
// console.log(this)
/**
* 判断是否为函数,不是函数的时候,抛出错误
*/
if (typeof this !== 'function') {
throw new Error("错误")
}
context = context || window
/**
* 在这 把 this赋值给context这个参数
* 但是要假设 context 上有一个方法
*/
context.fn = this
let result = null
// 判断是否有第二个canshu,也就是判断 它是否为空值
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
// 删除这个(用完删除)
delete context.fn
// console.log(result)
return result
}
console.log(fun.getName.myAplly(obj, [ 1, 2])) // 小明
3)bind方法的实现
function fun() {
console.log(this);
}
var obj = {
name: "why"
}
function bind(func, obj) {
return function () {
// arguments传递过来的 所有参数
console.log(arguments);
return func.apply(obj, arguments);
}
}
var bar = bind(fun, obj);
bar(); // obj对象
let fun = {
getName: function () {
return this.name
}
}
let obj = {
name: "小明"
}
Function.prototype.myBind = function (context) {
if(typeof this !== "function"){
throw new Error("错误")
}
const that = this
const args = [...arguments].slice(1)
return function Fn(){
if (this instanceof Fn){
return new that(...args, ...arguments)
}
return that.apply(context, args.concat(...arguments))
}
}
console.log(fun.getName.myBind(obj)()) // 小明
5、new绑定
/**
* new构造函数
* 1、this = 创建出来的对象
* 2、这个绑定过程,就是new绑定
* 3、添加属性的时候,也就是属性添加进 this = {} 中
*/
function Person(val1, val2){
this.val1 = val1
this.val2 = val2
}
let f1 = new Person("呵呵", "哈哈哈")
console.log(f1.val1, f1.val2) // 呵呵 哈哈哈
let f2 = new Person("age", 25)
console.log(f2.val1, f2.val2) // age 25
6、this绑定优先级
1)默认规则的优先级最低
2)显示绑定优先级高于隐式绑定
3)new绑定优先级高于隐式绑定
4)new绑定优先级高于bind(new绑定和call、apply不能一块使用,bind可以和new一块使用)
function fun(){
console.log(this) // fun{}
}
let fn = fun.bind("哈哈哈")
let obj = new fun()
总结:
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
四、内置函数的绑定
1、setTimeout
/**
* setTimeout相当于下面的回调函数
*/
setTimeout(function (){
console.log(this) // window
}, 2000)
// 1、函数
function fun(fn, duration) {
fn.call("指定this指向")
}
// 2、调用
fun(function () {
console.log(this)
}, 2000)
2、元素中的this指向
const box = document.querySelector("div")
// 把下面的这个点击传入给这个DOM元素作为属性使用
box.onclick = function (){
console.log(this) // this指向元素对象
/**
* 这个this的指向,相当于一个隐式绑定
* 元素对象.onclick()
*/
}
// 元素事件的监听(这个可以写多个,上面的就不可以写多个)
box.addEventListener("click", function (){
/**
* 调用的理解:
* 得到当前的函数,使用call改变函数中this的指向,让其指向当前元素
* 得到当前的函数.call(当前元素)
*/
console.log(this)
})
3、数组方法的this指向
let arr = [ 1, 2, 44, 66, "哈哈哈", "heheh " ]
/**
* forEach、map 入第二个参数的时候,就可改变this指向
*/
arr.forEach(function (item){
console.log(item, this)
}, "改变forEach的this的指向")
arr.map(function (item) {
console.log(item, this)
}, "改变map的this的指向")
五、this指向的特殊情况
1、this的忽略显示绑定
function fun(){
console.log(this)
}
fun.apply(null) // Window
fun.apply(undefined) //Window
fun.call(null) // Window
fun.call(undefined) //Window
fun.bind(null)() // Window
fun.bind(undefined)() //Window
let fn1 = fun.bind(null)
fn1() //Window
let fn2 = fun.bind(undefined)
fn2() //Window
2、this的间接函数引用
let obj1 = {
name: "obj1",
fun: function (){
console.log(this)
}
}
let obj2 = {
name: "obj2"
};
/**
* 把obj1的fun赋值给obj2的bar
* 打印的是obj2
*/
// obj2.bar = obj1.fun
// obj2.bar() //{name: 'obj2', bar: ƒ}
/**
* 下面把这个赋值写成自运行
* (obj2.bar = obj1.fun)() 这样的写 是无法运行的,无法运行的原因如下:
* 1、使用小括号括起来的话,你就把他看作为一个整体
* 2、无法运行的原因是 在obj2后面没加分号的话,他就会 理解他们是一个整体,类似下面的效果:
let obj2 = {
name: "obj2"
}(obj2.bar = obj1.fun)()
所以,你加上分号 就不报错了(具体的原因,请阅读: 你不知道的javascript)
* 3、所以在代码中使用小括号、中括号的时候,请查看自己的代码块 是否正确
* 4、在写代码的时候,请尽量加分号
*/
(obj2.bar = obj1.fun)() // Window
六、箭头函数
注:① 箭头函数不会绑定this、arguments属性;
② 箭头函数不能作为构造函数来使用(也就是不能和new 一块使用)
③ 箭头函数不适用于上面的四种绑定规则(new绑定、显示绑定、隐式绑定、默认绑定)
1、箭头函数的this根据外层作用域来决定
2、箭头函数中this的指向
let obj1 = {
data: "",
getData: function (){
console.log(this) // {data: '', getData: ƒ}
setTimeout(function (){
console.log(this) // Window
}, 2000)
}
}
obj1.getData()
let obj2 = {
data: "",
getData: () => {
console.log(this) // Window
setTimeout(function () {
console.log(this) // Window
}, 2000)
}
}
obj2.getData()
let obj3 = {
data: "",
getData: () => {
console.log(this) // Window
setTimeout(() => {
console.log(this) // Window
}, 2000)
}
}
obj3.getData()
可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。
let obj = {
name: "哈哈哈",
eating: function () {
/**
* 这块不使用this,也是可以的
* 但是如果 你 obj 改变了,下面就要改变,这样会很不方便(在开发过程中,要切记不要低代码开发)
*/
console.log(obj.name + "吃东西")
},
running: function () {
console.log(this.name + "运动")
},
studying: function () {
console.log(this.name + "学习")
}
}
obj.eating() // 哈哈哈吃东西
obj.running() // 哈哈哈运动
obj.studying() // 哈哈哈学习
二、this的作用域
1、在全局作用域下
注:在全局作用域下,this指向window
console.log(this)
console.log(window)
2、在node环境下,this的指向
1)在node下,指向空的对象
2)在node下执行(其实源码里面是call来实现的)
3、this在函数中的使用
注:this指向什么,跟函数所处的位置是没有关系的,跟函数 被调用的方式是有关系的;他是在运行时被绑定的
function fun(){
console.log(this)
}
/**
* 下面提供了三种调用方式
*/
fun()
let obj = {
name:"哈哈",
/**
* foo现在指向上面的函数
*/
foo: fun
}
obj.foo()
fun.apply("啊啊啊啊")
三、this的绑定规则
注:this的指向,与调用的位置(或方式)有关
1、默认绑定
注:独立函数调用,也就是在window下 函数名/变量名() 这样子去运行的;未加任何绑定(或者 .XXX() 去进行运行的绑定)
/**
* 独立函数调用,这个时候 指向window
* 下面的案例,一块不太好看,你可以挨个去运行,查看this的指向
*/
// 案例1
function fun1() {
console.log("fun1", this)
}
fun1() // Window
/**
* 下面都是 独立被调用
*/
// 案例2
function fun2() {
console.log("fun2", this)
}
function fun3() {
console.log("fun3", this)
fun2()
}
function fun4() {
console.log("fun4", this)
fun3()
}
fun4()
// 案例3 *
let obj = {
name: "哈哈哈",
fun5: function () {
console.log("fun5", this)
}
}
// obj.fun5() // 这个有调用主题(他不是 独立的,这个时候,它指向obj)
const fun6 = obj.fun5
fun6() // 指向window
// 案例4
function fun7(){
console.log("fun7", this)
}
let obj2 = {
name: "哈哈哈",
fun: fun7
}
// obj.fun5() // 这个有调用主题(他不是 独立的,这个时候,它指向obj)
const fun8 = obj2.fun
fun8() // 指向window
// 案例5
function fun9(){
function bar(){
console.log("fun9", this)
}
return bar
}
const fun10 = fun9()
fun10()
2、隐式绑定
注:通过某个对象,进行的函数调用
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this
1、与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在[严格模式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode)和非严格模式之间也会有一些差别。
2、在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 [bind](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES2015 引入了[箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions),箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。
```javascript
let obj = {
name: "哈哈哈",
eating: function () {
/**
* 这块不使用this,也是可以的
* 但是如果 你 obj 改变了,下面就要改变,这样会很不方便(在开发过程中,要切记不要低代码开发)
*/
console.log(obj.name + "吃东西")
},
running: function () {
console.log(this.name + "运动")
},
studying: function () {
console.log(this.name + "学习")
}
}
obj.eating() // 哈哈哈吃东西
obj.running() // 哈哈哈运动
obj.studying() // 哈哈哈学习
可查看下面这个案例(引用于MDN):
二、this的作用域
1、在全局作用域下
注:在全局作用域下,this指向window
console.log(this)
console.log(window)
2、在node环境下,this的指向
1)在node下,指向空的对象
2)在node下执行(其实源码里面是call来实现的)
3、this在函数中的使用
注:this指向什么,跟函数所处的位置是没有关系的,跟函数 被调用的方式是有关系的;他是在运行时被绑定的
function fun(){
console.log(this)
}
/**
* 下面提供了三种调用方式
*/
fun()
let obj = {
name:"哈哈",
/**
* foo现在指向上面的函数
*/
foo: fun
}
obj.foo()
fun.apply("啊啊啊啊")
三、this的绑定规则
注:this的指向,与调用的位置(或方式)有关
1、默认绑定
注:独立函数调用,也就是在window下 函数名/变量名() 这样子去运行的;未加任何绑定(或者 .XXX() 去进行运行的绑定)
/**
* 独立函数调用,这个时候 指向window
* 下面的案例,一块不太好看,你可以挨个去运行,查看this的指向
*/
// 案例1
function fun1() {
console.log("fun1", this)
}
fun1() // Window
/**
* 下面都是 独立被调用
*/
// 案例2
function fun2() {
console.log("fun2", this)
}
function fun3() {
console.log("fun3", this)
fun2()
}
function fun4() {
console.log("fun4", this)
fun3()
}
fun4()
// 案例3 *
let obj = {
name: "哈哈哈",
fun5: function () {
console.log("fun5", this)
}
}
// obj.fun5() // 这个有调用主题(他不是 独立的,这个时候,它指向obj)
const fun6 = obj.fun5
fun6() // 指向window
// 案例4
function fun7(){
console.log("fun7", this)
}
let obj2 = {
name: "哈哈哈",
fun: fun7
}
// obj.fun5() // 这个有调用主题(他不是 独立的,这个时候,它指向obj)
const fun8 = obj2.fun
fun8() // 指向window
// 案例5
function fun9(){
function bar(){
console.log("fun9", this)
}
return bar
}
const fun10 = fun9()
fun10()
2、隐式绑定
注:通过某个对象,进行的函数调用
// case1
function fun1(){
console.log(this)
}
let obj1 = {
age:222,
foo: fun1
}
obj1.foo() // obj1
let bar = obj1.foo
bar() // window
// case2
let obj2 = {
age:22,
foo: function () {
console.log(this, this.age + obj2.age)
}
}
obj2.foo() // {age: 22, foo: ƒ} 44
// case3
let obj3 = {
name: "obj3",
foo: function () {
console.log(this)
}
}
let obj4 = {
name: "obj4",
// 对象中的属性,指向函数
foo: obj3.foo
}
obj4.foo() // {name: 'obj4', foo: ƒ}
3、显示绑定
call、apply、bind 改变函数体内的this指向
语法:函数.call()、函数.apply()、函数.bind()
1)call
① call立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.call(obj) // 立即执行 {age: 22, name: '小明'} '小明'
fun() // Window ""
② call有多个参数时需要罗列(也就是按个书写)
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.call(obj, 222, "boy") // {name: '小明', age: 222, gender: 'boy'} '小明'
2)apply
① apply立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.apply(obj) // 立即执行
fun()
② apply的第二个参数是数组
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.apply(obj, [ 222, "boy" ])
3)bind
① bind不立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.bind(obj) // 不立即执行(他返回出来 是一个函数)
fun.bind(obj)() // 立即执行要jia 括号
fun()
② bind有多个参数时需要罗列(也就是按个书写)
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.bind(obj, 222, "boy")()
function fun(){
console.log(this)
}
/**
* 1、bind的返回值 是一个函数
* 2、bind是函数的方法
* 3、传入的参数,就是绑定的this
*/
let newFun = fun.bind("传入的参数(也就是绑定this),就是绑定的this")
newFun() // String {'传入的参数(也就是绑定this),就是绑定的this'}
4)call、apply、bind使用场景
let arr = [ 1, 2, 4, 6, 8, 11, 134, 0 ]
// console.log(Math.max(arr)) // NaN
/**
* 这块不涉及this的绑定,所以穿空
* 这块只能时apply,因为apply的第二个参数 是数组
* Math.XXX 的apply只能写在类似于下面的这个位置
*/
console.log(Math.max.apply(null ,arr)) // 134
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<input type="button" value="按钮">
<br>
<h2>内容</h2>
</body>
</html>
<script>
const but = document.querySelector("input")
const content = document.querySelector("h2")
/**
* 这块就要使用 bind ,因为 你不想让他自动触发
*/
but.onclick = function (){
console.log(this) // 指向h2
}.bind(content)
</script>
4、自己实现call、apply、bind方法
1)call的实现
let fun = {
getName: function () {
return this.name
}
}
let obj = {
name: "小明"
}
/**
* 1、手写call,要使用重新的这个 方法挂载到Function的属性上
* 2、这个里面的this时谁?
* 谁调用的他,这个this 就指向谁,这块的话,是getName调用的,所以 this就是 getName这个函数
*/
Function.prototype.myCall = function (context) {
// console.log(arguments)
// console.log(this)
/**
* 判断是否为函数,不是函数的时候,抛出错误
*/
if (Object.prototype.toString.call(this).match(/\[object (.*?)\]/)[1].toLowerCase() !== 'function') {
throw new Error("错误")
}
context = context || window
let args = [ ...arguments ].slice(1)
// console.log(args)
/**
* 在这 把 this赋值给context这个参数
* 但是要假设 context 上有一个方法
*/
context.fn = this
let result = context.fn(...args)
// console.log(result)
return result
}
console.log(fun.getName.myCall(obj)) // 小明
2)apply的实现
let fun = {
getName: function (a, b) {
console.log(a + b)
return this.name
}
}
let obj = {
name: "小明"
}
/**
* 1、手写call,要使用重新的这个 方法挂载到Function的属性上
* 2、这个里面的this时谁?
* 谁调用的他,这个this 就指向谁,这块的话,是getName调用的,所以 this就是 getName这个函数
*/
Function.prototype.myAplly = function (context) {
// console.log(arguments)
// console.log(this)
/**
* 判断是否为函数,不是函数的时候,抛出错误
*/
if (typeof this !== 'function') {
throw new Error("错误")
}
context = context || window
/**
* 在这 把 this赋值给context这个参数
* 但是要假设 context 上有一个方法
*/
context.fn = this
let result = null
// 判断是否有第二个canshu,也就是判断 它是否为空值
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
// 删除这个(用完删除)
delete context.fn
// console.log(result)
return result
}
console.log(fun.getName.myAplly(obj, [ 1, 2])) // 小明
3)bind方法的实现
function fun() {
console.log(this);
}
var obj = {
name: "why"
}
function bind(func, obj) {
return function () {
// arguments传递过来的 所有参数
console.log(arguments);
return func.apply(obj, arguments);
}
}
var bar = bind(fun, obj);
bar(); // obj对象
5、new绑定
/**
* new构造函数
* 1、this = 创建出来的对象
* 2、这个绑定过程,就是new绑定
* 3、添加属性的时候,也就是属性添加进 this = {} 中
*/
function Person(val1, val2){
this.val1 = val1
this.val2 = val2
}
let f1 = new Person("呵呵", "哈哈哈")
console.log(f1.val1, f1.val2) // 呵呵 哈哈哈
let f2 = new Person("age", 25)
console.log(f2.val1, f2.val2) // age 25
6、this绑定优先级
1)默认规则的优先级最低
2)显示绑定优先级高于隐式绑定
3)new绑定优先级高于隐式绑定
4)new绑定优先级高于bind(new绑定和call、apply不能一块使用,bind可以和new一块使用)
function fun(){
console.log(this) // fun{}
}
let fn = fun.bind("哈哈哈")
let obj = new fun()
总结:
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
四、内置函数的绑定
1、setTimeout
/**
* setTimeout相当于下面的回调函数
*/
setTimeout(function (){
console.log(this) // window
}, 2000)
// 1、函数
function fun(fn, duration) {
fn.call("指定this指向")
}
// 2、调用
fun(function () {
console.log(this)
}, 2000)
2、元素中的this指向
const box = document.querySelector("div")
// 把下面的这个点击传入给这个DOM元素作为属性使用
box.onclick = function (){
console.log(this) // this指向元素对象
/**
* 这个this的指向,相当于一个隐式绑定
* 元素对象.onclick()
*/
}
// 元素事件的监听(这个可以写多个,上面的就不可以写多个)
box.addEventListener("click", function (){
/**
* 调用的理解:
* 得到当前的函数,使用call改变函数中this的指向,让其指向当前元素
* 得到当前的函数.call(当前元素)
*/
console.log(this)
})
3、数组方法的this指向
let arr = [ 1, 2, 44, 66, "哈哈哈", "heheh " ]
/**
* forEach、map 入第二个参数的时候,就可改变this指向
*/
arr.forEach(function (item){
console.log(item, this)
}, "改变forEach的this的指向")
arr.map(function (item) {
console.log(item, this)
}, "改变map的this的指向")
五、this指向的特殊情况
1、this的忽略显示绑定
function fun(){
console.log(this)
}
fun.apply(null) // Window
fun.apply(undefined) //Window
fun.call(null) // Window
fun.call(undefined) //Window
fun.bind(null)() // Window
fun.bind(undefined)() //Window
let fn1 = fun.bind(null)
fn1() //Window
let fn2 = fun.bind(undefined)
fn2() //Window
2、this的间接函数引用
let obj1 = {
name: "obj1",
fun: function (){
console.log(this)
}
}
let obj2 = {
name: "obj2"
};
/**
* 把obj1的fun赋值给obj2的bar
* 打印的是obj2
*/
// obj2.bar = obj1.fun
// obj2.bar() //{name: 'obj2', bar: ƒ}
/**
* 下面把这个赋值写成自运行
* (obj2.bar = obj1.fun)() 这样的写 是无法运行的,无法运行的原因如下:
* 1、使用小括号括起来的话,你就把他看作为一个整体
* 2、无法运行的原因是 在obj2后面没加分号的话,他就会 理解他们是一个整体,类似下面的效果:
let obj2 = {
name: "obj2"
}(obj2.bar = obj1.fun)()
所以,你加上分号 就不报错了(具体的原因,请阅读: 你不知道的javascript)
* 3、所以在代码中使用小括号、中括号的时候,请查看自己的代码块 是否正确
* 4、在写代码的时候,请尽量加分号
*/
(obj2.bar = obj1.fun)() // Window
六、箭头函数
注:① 箭头函数不会绑定this、arguments属性;
② 箭头函数不能作为构造函数来使用(也就是不能和new 一块使用)
③ 箭头函数不适用于上面的四种绑定规则(new绑定、显示绑定、隐式绑定、默认绑定)
1、箭头函数的this根据外层作用域来决定
2、箭头函数中this的指向
let obj1 = {
data: "",
getData: function (){
console.log(this) // {data: '', getData: ƒ}
setTimeout(function (){
console.log(this) // Window
}, 2000)
}
}
obj1.getData()
let obj2 = {
data: "",
getData: () => {
console.log(this) // Window
setTimeout(function () {
console.log(this) // Window
}, 2000)
}
}
obj2.getData()
let obj3 = {
data: "",
getData: () => {
console.log(this) // Window
setTimeout(() => {
console.log(this) // Window
}, 2000)
}
}
obj3.getData()
// case1 function fun1(){ console.log(this) } let obj1 = { age:222, foo: fun1 } obj1.foo() // obj1 let bar = obj1.foo bar() // window
// case2 let obj2 = { age:22, foo: function () { console.log(this, this.age + obj2.age) } } obj2.foo() // {age: 22, foo: ƒ} 44
// case3 let obj3 = { name: "obj3", foo: function () { console.log(this) } } let obj4 = { name: "obj4", // 对象中的属性,指向函数 foo: obj3.foo } obj4.foo() // {name: 'obj4', foo: ƒ}
## 3、显示绑定
call、apply、bind 改变函数体内的this指向
语法:函数.call()、函数.apply()、函数.bind()
### 1)call

① call立即执行
```javascript
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.call(obj) // 立即执行 {age: 22, name: '小明'} '小明'
fun() // Window ""
② call有多个参数时需要罗列(也就是按个书写)
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.call(obj, 222, "boy") // {name: '小明', age: 222, gender: 'boy'} '小明'
2)apply
① apply立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.apply(obj) // 立即执行
fun()
② apply的第二个参数是数组
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.apply(obj, [ 222, "boy" ])
3)bind
① bind不立即执行
let name = "你好"
let obj = { age: 22, name: "小明" }
function fun(){
console.log(this, this.name)
}
fun.bind(obj) // 不立即执行(他返回出来 是一个函数)
fun.bind(obj)() // 立即执行要jia 括号
fun()
② bind有多个参数时需要罗列(也就是按个书写)
let name = "你好"
let obj = { name: "小明" }
function fun(age, gender){
// 这个 是传入的
this.age = age
this.gender = gender
console.log(this, this.name)
}
fun.bind(obj, 222, "boy")()
function fun(){
console.log(this)
}
/**
* 1、bind的返回值 是一个函数
* 2、bind是函数的方法
* 3、传入的参数,就是绑定的this
*/
let newFun = fun.bind("传入的参数(也就是绑定this),就是绑定的this")
newFun() // String {'传入的参数(也就是绑定this),就是绑定的this'}
4)call、apply、bind使用场景
let arr = [ 1, 2, 4, 6, 8, 11, 134, 0 ]
// console.log(Math.max(arr)) // NaN
/**
* 这块不涉及this的绑定,所以穿空
* 这块只能时apply,因为apply的第二个参数 是数组
* Math.XXX 的apply只能写在类似于下面的这个位置
*/
console.log(Math.max.apply(null ,arr)) // 134
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<input type="button" value="按钮">
<br>
<h2>内容</h2>
</body>
</html>
<script>
const but = document.querySelector("input")
const content = document.querySelector("h2")
/**
* 这块就要使用 bind ,因为 你不想让他自动触发
*/
but.onclick = function (){
console.log(this) // 指向h2
}.bind(content)
</script>
4、自己实现bind方法
function fun() {
console.log(this);
}
var obj = {
name: "why"
}
function bind(func, obj) {
return function () {
// arguments传递过来的 所有参数
console.log(arguments);
return func.apply(obj, arguments);
}
}
var bar = bind(fun, obj);
bar(); // obj对象
5、new绑定
/**
* new构造函数
* 1、this = 创建出来的对象
* 2、这个绑定过程,就是new绑定
* 3、添加属性的时候,也就是属性添加进 this = {} 中
*/
function Person(val1, val2){
this.val1 = val1
this.val2 = val2
}
let f1 = new Person("呵呵", "哈哈哈")
console.log(f1.val1, f1.val2) // 呵呵 哈哈哈
let f2 = new Person("age", 25)
console.log(f2.val1, f2.val2) // age 25
6、this绑定优先级
1)默认规则的优先级最低
2)显示绑定优先级高于隐式绑定
3)new绑定优先级高于隐式绑定
4)new绑定优先级高于bind(new绑定和call、apply不能一块使用,bind可以和new一块使用)
function fun(){
console.log(this) // fun{}
}
let fn = fun.bind("哈哈哈")
let obj = new fun()
总结:
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
四、内置函数的绑定
1、setTimeout
/**
* setTimeout相当于下面的回调函数
*/
setTimeout(function (){
console.log(this) // window
}, 2000)
// 1、函数
function fun(fn, duration) {
fn.call("指定this指向")
}
// 2、调用
fun(function () {
console.log(this)
}, 2000)
2、元素中的this指向
const box = document.querySelector("div")
// 把下面的这个点击传入给这个DOM元素作为属性使用
box.onclick = function (){
console.log(this) // this指向元素对象
/**
* 这个this的指向,相当于一个隐式绑定
* 元素对象.onclick()
*/
}
// 元素事件的监听(这个可以写多个,上面的就不可以写多个)
box.addEventListener("click", function (){
/**
* 调用的理解:
* 得到当前的函数,使用call改变函数中this的指向,让其指向当前元素
* 得到当前的函数.call(当前元素)
*/
console.log(this)
})
3、数组方法的this指向
let arr = [ 1, 2, 44, 66, "哈哈哈", "heheh " ]
/**
* forEach、map 入第二个参数的时候,就可改变this指向
*/
arr.forEach(function (item){
console.log(item, this)
}, "改变forEach的this的指向")
arr.map(function (item) {
console.log(item, this)
}, "改变map的this的指向")
五、this指向的特殊情况
1、this的忽略显示绑定
function fun(){
console.log(this)
}
fun.apply(null) // Window
fun.apply(undefined) //Window
fun.call(null) // Window
fun.call(undefined) //Window
fun.bind(null)() // Window
fun.bind(undefined)() //Window
let fn1 = fun.bind(null)
fn1() //Window
let fn2 = fun.bind(undefined)
fn2() //Window
2、this的间接函数引用
let obj1 = {
name: "obj1",
fun: function (){
console.log(this)
}
}
let obj2 = {
name: "obj2"
};
/**
* 把obj1的fun赋值给obj2的bar
* 打印的是obj2
*/
// obj2.bar = obj1.fun
// obj2.bar() //{name: 'obj2', bar: ƒ}
/**
* 下面把这个赋值写成自运行
* (obj2.bar = obj1.fun)() 这样的写 是无法运行的,无法运行的原因如下:
* 1、使用小括号括起来的话,你就把他看作为一个整体
* 2、无法运行的原因是 在obj2后面没加分号的话,他就会 理解他们是一个整体,类似下面的效果:
let obj2 = {
name: "obj2"
}(obj2.bar = obj1.fun)()
所以,你加上分号 就不报错了(具体的原因,请阅读: 你不知道的javascript)
* 3、所以在代码中使用小括号、中括号的时候,请查看自己的代码块 是否正确
* 4、在写代码的时候,请尽量加分号
*/
(obj2.bar = obj1.fun)() // Window
六、箭头函数
注:① 箭头函数不会绑定this、arguments属性;
② 箭头函数不能作为构造函数来使用(也就是不能和new 一块使用)
③ 箭头函数不适用于上面的四种绑定规则(new绑定、显示绑定、隐式绑定、默认绑定)
1、箭头函数的this根据外层作用域来决定
2、箭头函数中this的指向
let obj1 = {
data: "",
getData: function (){
console.log(this) // {data: '', getData: ƒ}
setTimeout(function (){
console.log(this) // Window
}, 2000)
}
}
obj1.getData()
let obj2 = {
data: "",
getData: () => {
console.log(this) // Window
setTimeout(function () {
console.log(this) // Window
}, 2000)
}
}
obj2.getData()
let obj3 = {
data: "",
getData: () => {
console.log(this) // Window
setTimeout(() => {
console.log(this) // Window
}, 2000)
}
}
obj3.getData()