constructor
、prototype
、__proto__
:constructor
和__proto__
是对象的属性,constructor
指向对象的构造函数,__proto__
是浏览器对 ES 标准[[Prototype]]
的实现,指向对象的构造函数的prototype
对象;prototype
是函数的属性,指向该函数的prototype
对象。- 重写原型对象:当通过将一个新对象字面量赋值给构造函数的
prototype
属性时,实际上是在替换原有的原型对象。这样做会导致构造函数与最初的原型对象之间的连接断开,尤其是constructor
属性不再指向原来的构造函数。这是因为在 JavaScript 中,每个构造函数都有一个默认的prototype
属性,该属性最初指向一个空对象,这个对象有一个默认的constructor
属性指向该构造函数。如果重写了prototype
,却没有显式地设置新原型对象的constructor
属性,那么新原型对象的constructor
属性将会默认指向Object
构造函数。 new
关键字的工作原理:
- 创建新对象;
- 将新对象的__proto__指向构造函数的prototype;
- 通过apply或call执行构造函数,使this指向新对象;
- 判断构造函数的返回值是否为对象,是直接返回,否返回新对象。
this
指向:
- 当函数作为对象的方法被调用时,
this
指向该对象; - 当函数不作为对象的属性被调用,而是普通的函数方式,
this
非严格模式指向全局对象
,严格模式下指向undefined
; - 当用
new
运算调用函数时,该函数总会返回一个对象,构造器里的this
指向返回的这个对象; - 用
Function.prototype.call
或Function.prototype.apply
可以动态地改变传入函数的this
指向。 - 箭头函数没有自己的
this
绑定,它继承自父执行上下文的this
值; - 事件处理函数中的
this
通常指向触发事件的元素;
apply
接受两个参数,第一个参数指定了函数this
的指向,第二个参数为传递给被调用函数的参数集合;call
接受的参数个数不固定,第一个参数指定了函数this
的指向,后面的参数被依次传入函数;bind
不会修改原函数的this
指向,它会创建一个新函数,新函数被调用时,bind
的第一个参数作为它的this
,之后参数的个数不固定。- ES6 中的
类
:
extends
关键字:用于定义一个类继承自另一个类。它创建了一个子类,子类可以继承父类的所有公共属性和方法。super
关键字:- 调用父类的构造函数:在子类的构造函数中,必须使用
super
来调用父类的构造函数。这是因为子类需要初始化它从父类继承的属性。 - 调用父类的方法:在子类的方法中,可以使用
super
来调用父类中同名的方法。这在需要扩展或修改父类行为时非常有用。
- 调用父类的构造函数:在子类的构造函数中,必须使用
- 静态属性:使用
static
关键字定义,不属于类的实例,而是属于类本身,只能通过类进行调用。 - 私有属性:在类的内部使用,外部无法直接访问的属性。ES2022 之前,JavaScript 中没有真正的私有属性,但可以通过闭包或者命名约定(如属性名前加_)来模拟私有属性。ES2022 引入了
#
前缀来定义私有属性。
- ES5 的继承是通过
prototype
或构造函数
机制来实现。ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this
上:Parent.apply(this)
。ES6 的继承机制完全不同,实质上是先创建父类的实例对象this
,所以必须先调用父类的super()
方法,然后再用子类的构造函数修改this
。 - JS 继承:
原型链
继承:将子类的原型设置为父类的实例,这时子类的构造函数也被指向了父类,再将子类的构造函数指回子类。
缺点:原型上的引用类型是共用的;子类实例无法获取父类构造函数中定义的属性;深度嵌套的原型链,会导致属性和方法的查找效率下降。
构造函数
继承:在子类的构造函数中通过call调用父类的构造函数。
缺点:子类实例无法访问父类原型上的方法;每次创建子类示例时,父类构造函数都会被执行。组合式
继承:既使用构造函数继承来继承父类构造函数中的属性,又使用原型链继承来继承父类原型上的属性和方法。
缺点:并未解决原型链继承嵌套过深和构造函数继承每次创建子类实例都会支持父类构造函数的问题;同时父类构造函数执行了两次,分别在执行父类构造函数时、将子类的原型设置为父类的实例时。寄生组合式
继承:与组合式继承的区别在于,原型链继承时不直接将子类原型指向父类实例,而是通过Object.create创建一个临时对象,该临时对象的原型指向父类原型,将临时对象的构造函数指向子类构造函数,然后将子类原型指向该临时对象。解决了组合式继承中父类构造函数调用两次的问题。