为什么要使用this
// 假设对象名称user改为info
const info = {
id: 1,
name: 'luckyCoder',
address: '猿星',
// 使用this
eating: function() {
console.log(`${this.name}在${this.address}吃东西~`)
},
// 不使用this
running: function() {
// 此处也需要同步进行修改为info
console.log(`${info.name}在${info.address}跑步~`)
}
}
info.eating() // luckyCoder在猿星吃东西~
info.running() // luckyCoder在猿星跑步~
如果在不使用this的情况下修改了对象的名称,那我们必须将其内部的方法所使用的名称同步更改,而使用this则不需要考虑这个问题
this的指向
this是个关键字,它的值是在函数调用的时候确定的(函数也相当于一个对象), 严格模式下指向undefined
默认绑定
function foo() {
function bar() {
console.log(this)
}
return bar
}
const fn = foo()
fn() // 独立的函数调用 this => window
无论函数定义时通过哪些复杂的方式,只要调用的时候是独立的,就符合this默认绑定的规则,指向window
var name = '林一一'
var obj = {
name : '二二',
callback: function(){
console.log(this.name)
}
}
setTimeout(obj.callback,1000)
/* 输出
* 林一一
*/
setTimeout函数的延迟执行函数中的this指向window,若延迟执行函数是箭头函数this指向其外层的上下文
隐式绑定
const obj1 = {
name: 'obj1',
foo: function() {
console.log(this)
}
}
const obj2 = {
name: 'obj2',
bar: obj1.foo
}
obj2.bar() // 通过对象点语法的方式调用函数 this => obj2对象
通过object.fn()的方式调用某个函数时,object对象会被JS引擎绑定到fn函数中的this,这个绑定的过程是内部自动绑定,无法看到绑定过程,称为隐式绑定
var name = '林二二'
var obj = {
name: '林一一',
fn: function () {
var name = '小三'
return this.name
}
}
console.log(obj.fn()) // '林一一'
var fo = obj.fn
console.log(fo()) // '林二二' fo() ==> window.fo()
console.log(obj.fn()()) // '林二二'
(1)obj.fn()中函数fn()作为对象方法调用,this指向obj对象
(2)当调用函数作为参数传递或者变量赋值给另一个变量,this指向全局
(3)obj.fn()执行完后有一个函数返回,最后相当于执行函数A(), this指向全局
显示绑定
不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用?在JS中所有的函数都可以使用call,apply和bind方法,通过这些方法实现显示绑定规则
function foo() {
console.log(this)
}
const obj = {
name: "obj"
foo: foo // 通过foo.apply(obj)调用时 这段代码可以省略
}
// 通过对隐式绑定的学习 我们知道 如果希望foo函数被调用时this是指向obj的
// 那么我们需要给obj添加一个foo属性并且属性值指向foo函数
// 再进行对象点语法的方式调用
// 从而达到隐式绑定规则 this则指向obj
obj.foo() // this => obj
// 那么我们也可以使用call/apply的方式来实现显示绑定规则
// 并且也无需在obj中创建foo属性
foo.apply(obj) // 显示绑定规则 this => obj
// call/apply是可以指定this的绑定对象
call,apply,bind修改this的指向
共同点:
(1)改变this的指向
(2)三者的第一个参数都是this要指向的对象,如果没有默认全局window
不同点:
(1)调用方式
call: 可以直接调用函数,并传递参数列表,立即执行
apply:可以直接调用函数,参数以数组或类数组的形式传递,立即执行
bind: bind可以分多次传入参数,是返回绑定this之后的函数,apply,call则是立即执行
构造函数的this
new的作用
function Test(name) {
this.name = name
}
Test.prototype.sayName = function() {
console.log(this.name)
}
const t = new Test('yck')
console.log(t.name)
t.sayName()
(1)new通过构造函数Test创建出来的实例可以访问构造函数中的属性 (2)new通过构造函数Test创建出来的实例可以访问到构造函数原型链中的属性 (3)new操作符,实例与构造函数通过原型链连接起来
function Test1(name) {
this.name = name
return 1
}
const t1 = new Test1('yck')
console.log('1',t1.name)
(1) 构造函数如果返回原始值,返回值毫无意义
function Test2(name) {
this.name = name
return { age: 26 }
}
const t2 = new Test2('yck')
console.log(t2) // { age: 26 }
console.log(t2.name) // undefined
(2) 构造函数如果返回值为对象,这个返回值会被正常返回
当使用new关键字调用函数时,如果构造函数返回这个对象,this指向这个对象,如果返回基本数据或者没有返回,this指向实例,尽量不要返回值。因为返回原始值不会生效
new操作符的执行过程
1.创建一个空对象,
2.将空对象的_proto_属性指向构造函数的原型对象
3.将构造函数的this绑定到新的对象上
4.根据构造函数返回的类型判断返回值
// 第一个参数是构造函数,第二个参数是构造函数的参数
function newFun() {
let obj = {}
// 将构造函数的作用域赋给新对象,
// 获取构造函数即传入的第一个参数,arguments 是类数组,我们不能直接使用 shift 方法,我们可以使用 call 来调用 Array 上的 shift 方法,获取 fn
let fn = Array.prototype.shift.call(arguments)
// 把obj的原型指向构造函数的原型对象上 Object.setPropertyOf(obj, fn.prototype)
obj.__proto__ = fn.prototype
// 为这个新对象添加属性,将obj绑定到构造函数上, 并且传入剩余的参数
let result = fn.apply(obj,arguments)
// 如果返回值是对象,则返回,否则默认返回新对象,这样就实现了忽略构造函数返回的原始值
return result instanceof Object ? result: obj
}
优先级
function Person(name, age) {
// 往创建的对象中添加属性
this.name = name
this.age = age
// 因构造函数被调用了两次 产生了两个新对象
// 第一次this为所创建出的a对象
// 第二次this为所创建出的b对象
console.log(this)
}
// 调用构造函数并传入参数 返回新的对象
const a = new Person('a', 18)
const b = new Person('b', 20)
console.log(a) // => {name: 'a', age: 18}
console.log(b) // => {name: 'b', age: 20}
this绑定规则的优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
箭头函数
const foo = () => {
// 等同于我们在函数中使用某个变量
// 在函数中找不到的话则沿着作用域链向上查找
console.log(this)
}
const obj = {
foo: foo
}
const info = {}
foo() // this => window
obj.foo() // this => window
foo.call(info) // this => window
const f = new foo() // 箭头函数不可以使用new关键字进行调用 报错