this
js中的this是一个指针,指向一片作用域,指向哪里,跟编译过程无关,在运行的时候绑定的上下文执行环境
关于构造函数
new 一个实例的过程
- 创建一个空对象
将基类的原型赋值给实例的原型proto上
obj.__proto__ = fn.prototype
或
Object.setPrototypeOf(obj, fn.prototype)
调用基类的构造函数,this的在执行过程中指向新创建的对象,执行构造函数的代码
fn.apply(obj, args)
手写全过程
function defineNew(fn, ...args) {
const obj = {}
obj.__proto__ = fn.prototype
fn.apply(obj, args)
return obj
}
测试
function Person(name,age){
this.name = name;
this.age = age;
this.speak = function(){
console.log('im '+this.name+',im '+this.age+' years old')
}
}
let my = creatObj(Person,'gromy',18)
my.__proto__ == Person.prototype
true
执行上下文
全局执行上下文: 这是默认的、最基础的执行上下文。不在任何函数中的代码都位于全局执行上下文中。它做了两件事:1. 创建一个全局对象,在浏览器中这个全局对象就是 window 对象。2. 将 this 指针指向这个全局对象。一个程序中只能存在一个全局执行上下文。
函数执行上下文: 每次调用函数时,都会为该函数创建一个新的执行上下文。每个函数都拥有自己的执行上下文,但是只有在函数被调用的时候才会被创建。一个程序中可以存在任意数量的函数执行上下文。每当一个新的执行上下文被创建,它都会按照特定的顺序执行一系列步骤。
下文准备
let cat = {
name:'tom',
eat:'fish',
speak:function(speak,like){
console.log('im '+this.name+',im eat '+this.eat);
console.log('im speak '+speak+' ,i like '+like);
}
}
let dog = {
name:'gromit',
eat:'meat'
}
Call
使用
cat.speak.call(dog,'wangwang','play ball')
执行结果
im gromit,im eat meat
im speak wangwang ,i like play ball
原理
将this的指向到从调用者 转移到 目标对象上,执行并返回结果
代码实现
Function.prototype.gromyCall = function(target,...args){
//此时this是调用者,Function
let context = Symbol('this') //避免key冲突
target[context] = this //把调用者的作用域地址送给目标的一个属性
return target[context](...args) //执行并返回
}
Apply
使用
cat.speak.call(dog,['wangwang','play ball'])
原理
跟call 一样,把调用者的this 地址送给 目标对象的一个属性,区别在参数的形式
代码实现
Function.prototype.gromyApply = function(target,args){
let context = Symbol('this');
target[context] = this
return target[context](...args)
}
bind
使用
cat.speak.bind(dog,'wangwang','play ball')()
原理
this的改变和call,apply相同,不同的是bind没有执行过程,返回具有调用函数相同构造器的函数
代码实现
Function.prototype.gromyBind = function(target,...args){
let context = Symbol('this')
target[context] = this
let result = function(){
return target[context](...args)
}
if(this.prototype){ //保证
result.prototype = this.prototype
}
return result
}