this,前端三座大山之一
学会this
-
前端不论是学习框架还是学习新的东西,都需要前置知识,所以学好this,为今后持续学习打好基础
-
这里只讨论JavaScript的
this,它是JavaScript中的一个关键字 -
同时也是当前环境执行期上下文对象的一个属性
-
this在不同环境下表现是不同的,例如浏览器环境、node环境,这些未来慢慢补充
1. window和this的关系
- 全局作用域下的this -> 全局对象
console.log(this === window) // true全局作用域下this等于window
this.a = 1
this.b = function(){
return 'hello world'
}
console.log(window.a // 1
console.log(b) // f(){return 'hello world'}
this在浏览器、node环境下的表现是什么
-
先搞清楚不同环境下获取全局对象的方法:
-
web:
window、self、frames、this -
node:
global -
worker:
self -
通用:
globalThis可在任何环境拿到全局对象- globalThis是通用的,所以可以在有无窗口的环境放心使用它。
-
var a = 'global -> a'
global.b = 'global -> b'
var obj = {
a: 'obj -> a'
test: function(){
console.log(this.a) // 浏览器:('obj -> a') node:('obj -> a')
console.log(global.b) // 浏览器:(报错) node:('global -> b')
console.log(global.a) // 浏览器:(报错) node:(undefined)
console.log(self.a) // 浏览器:('global -> a') node:(报错)
console.log(frames.a) // 浏览器:('global -> a')') node:(报错)
console.log(globalThis.a) // 浏览器:('global -> a') node:(global -> a)
}
}
obj.test()
- 还要搞清楚在函数中,谁调用函数,函数内部指向默认就是谁
function test() {
'use strict'
return this;
}
console.log(test()) // undefined
console.log(window.test()) // window{}
2. 类
类的本质其实就是函数
// 类 class -> 容器/作用域/模块 -> 其实就是一个壳子
class Test{
constructor(){}
say(){console.log('hi')}
static do(){}
}
// 函数
const Test = (function(){
function Test2(){} // construcotr
Test.prototype.say = function(){}
Test.do = function(){}
window.Test = Test
})()
- 上面class和下面函数this指向几乎相等
类的静态方法和动态方法
class Test{
constructor(){
// 类的非静态方法 -> new -> this -> {}
this.test = function(){
console.log('none-static:' + this)
}
}
// 类的静态方法 -> Test.prototype{...}
// new this -> {} -> __proto__ -> Test.prototype 一层一层找直到Object.prototype
// Test -> prototype{} -> test方法
test(){
console.log('hi')
}
}
const test = new Test();
test.test()//'none-static:'[object,object]
类的super
class Father{
constructor(){
// new -> this -> {} -> age属性
this.age = 44
}
swim(){
console.log('Go swimming!!')
}
}
class Son extends Father{
constructor(){
// 调用Father上的constructor
// 生成this绑定 -> Father this -> Son的实例
// this -> new Father() -> {age}
super()
// {age.hobby}
this.hobby = 'traval'
}
study(){
console.log(this)
this.swim()
}
}
const son = new Son()
son.study()
3. 改变this指向
- bind、apply、call
var obj1 = {
a: 1
}
var obj2 = {
a:100
}
var a = 2;
function test(b,c){
console.log(this.a);
}
test() // 2
test.call(obj) // 1 实参直接写后面
test.apply(obj) // 1 传数组
- bind只改变一次this指向
var test1 = test.bind(obj1)
test1() // 1
var test2 = test1.bind(obj2)
test2() // 1
var t = test.bind(obj1).bind(obj2)
t() // 1
// 只有第一次bind生效
4.箭头函数
- 箭头函数是忽略任何形式的this指向的改变(bind、apply、call)都不能改变
- 箭头函数一定不是构造器,new一个箭头函数会报错
- 箭头函数中的this不是谁绑定就指向谁,而是始终指向外部作用域的this指向(外层)
const obj = {
a: 1,
b: 2
}
obj.test = () => {
console.log(obj); // obj{...}
console.log(this); // window{}
}
5.对象
- 对象方法内部的this指向最近的引用,原则上
const obj = {
a: 1,
b: 2,
test: function(){
// 指向obj
console.log(this.a);
},
test2: test2,
c: {
d: 3,
e: 4,
test3(){
// 指向obj.c
console.log(this.d);
}
}
}
function test2(){
console.log(this.b);
}
obj.test() // 1
obj.test2() // 2
obj.c.test3() // 3
- 对于孤立的函数,它的this就是window
const obj = {
a: 1,
b: 2,
test(){
function t(){
// 最近的引用就是window,因为它是孤立的
console.log(this);
}
t()
}
}
obj.test() // window
- 谁调用,window就指向谁
const a = {
test: function(){
console.log(this)
}
}
a.test() // a
let b = a.test
b() // window
6. Object.defineProperty()
创建对象的方法
- 字面量方法定义对象
function Object()
var obj = {
a: 1,
b: 2
}
-
Object.create- 可用于创建无链的对象
Object.create(null)
- 可用于创建无链的对象
var obj2 = Object.create({
a: 1,
b: 2
})
-
Object.defineProperty- 相当于拦截器,在访问和变更时会经过get和set方法
var obj3 = {}
Object.defineProperties(obj3,'a',{
get:function(){
console.log(this) // 指向Obj3
return 4
}
})
-
构造函数
-
new的过程
- 创建一个空对象
- 绑定原型
- this指向新造的空对象
- 在对象里把构造函数里为this赋的值添加到新对象里
- 把这个新造的对象隐式的返回出去
-
如果在构造函数里显示的返回了其他东西,那构造出来的对象就是这个其他的东西
-
function Test(){
this.a = 1;
this.b = 2
console.log(this);
}
// this -> 实例化出来的对象
new Test()
7.DOM、BOM
-
事件处理函数内部的this,总是指向被绑定的DOM元素
-
往往在事件处理函数内想拿到预期的this有两种方法
- 用
bind()把外部的this传进去 - 在外部保存this,然后再事件处理函数里使用
- 用