class类和构造函数
实例属性、静态方法和静态属性
extends
使用extends可以实现继承
可以改写继承到的属性或方法 同名覆盖
super
作为函数调用
作为对象使用
使用super的时候,必须显示指定作为函数还是作为对象使用
super 作为函数调用
super 代表父类的构造方法,只能用在子类的构造方法中 内部的 this 指向子类的实例
super 作为对象使用
在构造方法和一般方法中使用 super 代表父类的原型对象
通过 super 调用父类的方法时,方法的 this 指向当前的 子类实例
super 作为对象使用
在静态方法中使用 super 代表父类
通过 super 调用父类的方法时,方法的 this 指向当前的 子类
super 作为函数调用 当super 作为函数调用时, 它代表的指向的是父类的构造函数,在子类的构造函数必须执行一次 super 函数 也就是说,在子类继承父类中,如果super作为函数调用,只能写在子类的构造函数(constructor)里面,super代表的是父类的构造函数 class A { // class关键字声明了一个类A constructor() { } } class B extends A { // class关键字声明了B继承自A类 constructor() { // constructor构造器函数 super(); // 调用super() } } new A() // A new B() // B 复制代码在上面的代码中,子类 B 的构造函数之中的super(),它代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。 注意 super虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是 B 的实例 这里的super相当于 A 类的constructor构造函数,会执行 A 的constructor,但是此时的this指向的是 B,所以打印出 B 换一种理解是:在执行super时,A 把 constructor 方法给了 B,此时 B 有了 A 的功能,但是执行的是 B 的内容,也就是 es5 的A.prototype.constructor.call(this) 而作为函数调用:它必须只能在子类的构造器函数(constructor)中进行调用的,如果放在其他地方,则是会报错的,如下所示: class A {} class B extends A { fun() { super(); // 报错,super()不能放置在函数内调用 } } 复制代码在上面代码中,super()用在 B 类的fun方法之中,就会造成语法错误 super 作为对象使用 super作为对象使用时,分为在普通方法中使用和在静态方法中使用 普通方法使用:super指向父类的原型,即A.prototype,可以访问到原型上的方法和属性,也就是指向它父类的原型对象 class Animal { // class关键字声明了一个Animal类 constructor() { } parent(){ // 类Animal的一个方法 console.log('我是父类Animal的普通方法... ...'); } } class Dog extends Animal { // class关键字定义了Dog继承自Animal类 constructor() { super() } watchHome(){ // 子类watchHome为Dog的方法 super.parent() // 我是父类的普通方法 // 等价于Animal.prototype.parent() // // 我是父类的普通方法 } } const dog = new Dog() dog.watchHome() 复制代码在上面代码中,子类 Dog 当中的 super.parent(), 就是将 super 当做一个对象使用, 此时, super在普通方法之中, 指向A.prototype, 所以super.parent()就相当于 Animal.prototype.parent() 注意 由于 super 指向父类的原型对象, 所以定义在父类实例上的方法或属性, 是无法通过super 调用的 class Animal { constructor() { this.name = '父类上的实例上的属性' } } class Dog extends Animal { watchHome(){ return super.name } } const dog = new Dog() console.log(dog.watchHome()); // undefined,定义在父类实例上的属性, 无法通过super调用 复制代码在上面代码中 name 是父类Animal实例的属性, super.name 就引用不到它 但如果属性是定义在父类的原型对象上,super就可以取到,如下所示 class Animal { } Animal.prototype.name = 'super实例上的属性' class Dog extends Animal { watchHome() { return super.name } } const dog = new Dog() console.log(dog.watchHome()); // super实例上的属性,可以拿到父类中原型下的name属性,定义在原型下的属性和方法都是公有的 复制代码而在ES6中规定, 在子类普通方法中, 通过 super 调用父类的方法时, 方法内部的 this 指向当前子类的实例. class A { constructor() { this.name = "itclanCoder"; // 定义在A类的私有属性 } print() { // 定义在A类的print私有方法 console.log(this.name); } } class B extends A { // 类B继承自类A constructor() { super(); // 调用父类的super this.name = "随笔川迹"; // 子类B的私有属性 } m() { super.print(); } } let b = new B(); b.m() // 随笔川迹 复制代码super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()内部的this指向子类 B 的实例 导致输出的是"随笔川迹",而不是"itclanCoder"。也就是说,实际上执行的是super.print.call(this) 这个特性很有用,可以用于重写(覆盖)父类的私有属性 由于this此时指向子类实例,所以如果通过super对某个属性赋值,这时 super 就是 this,赋值的属性会变成子类实例的属性 class A { constructor() { this.name = "itclanCoder"; } } class B extends A { constructor() { super(); this.name = "itclan"; // 子类B的私有属性 super.name = "川川"; console.log(super.name); // undefined,super是父类,而name是父类的私有属性,无法直接访问 console.log(this.name); // 川川 } } let b = new B(); 复制代码在上面面示例代码中,super.name赋值为"川川",这时等同于对this.name赋值为川川。而当读取super.name的时候,它读的是A.prototype.name,所以返回undefined 如果用在静态方法之中 如果 super作为对象,用在静态方法之中,这时super将指向父类 , 而不是父类的原型对象 class Parent { static myMethod(msg) { // 父类的方法之前加静态static关键字 console.log('static1', msg); } myMethod(msg) { // 父类的私有普通方法 console.log('instance2', msg); } } class Child extends Parent { // 类child继承自篇Parent static myMethod(msg) { // 子类的私有myMethod方法前声明static super.myMethod(msg); // super在静态方法中指向父类, 而不是父类的原型 } myMethod(msg) { super.myMethod(msg); // super在普通方法中指向父类的原型 } } Child.myMethod(1); // static1 1 var child = new Child(); child.myMethod(2); // instance2 2 复制代码上面代码中,super 在静态方法之中指向父类,在普通方法之中指向父类的原型对象 另外,在子类的静态方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类而不是子类的实例 如下代码所示 class A { constructor() { this.x = 1; } static print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } static m() { super.print(); } } B.x = 3; B.m() // 3 复制代码上面代码中,静态方法B.m里面,super.print指向父类的静态方法。这个方法里面的this指向的是 B,而不是 B 的实例 注意 当使用 super 的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错 class A {} class B extends A { constructor() { super(); console.log(super); // 报错 } } 复制代码在上面代码中,console.log(super)当中的super,是无法看出是作为函数使用,还是作为对象使用,所以 JavaScript 引擎解析代码的时候就会报错。这时,如果你能清晰地表明 super 的数据类型,就不会报错 class A {} class B extends A { constructor() { super(); console.log(super.valueOf() instanceof B); // true } } let b = new B(); 复制代码在上面代码中,super.valueOf()表明 super 是一个对象,因此就不会报错。同时,由于 super 使得 this 指向 B 的实例,所以super.valueOf()返回的是一个 B 的实例 instanceof:的作用是A instanceof B,A 是否是由 B 实例化出来的,若是则为true,若不是则为false
new 构造函数
1 内存中创建一个新的空对象{}
2 让函数中的this指向空对象 this === {}
3 开始执行函数体 { age: 22 , name: "小明' }
4 返回该对象 (构造函数中不需要写return)
自定义构造函数和简单工厂函数的对比
1.函授首字母大写用于区别构造函数和普通函数
2.创建对象的过程是由new关键字实现
3.在构造函数内部会自动的创建新对象,并赋值给this指针
4.自动返回创建出来的对象
function Person(name, age, sex) { // 1.自动创建一个空对象,把这个对象的地址 this --> 新对象 // var this = new Object() // 2.this给空对象 绑定属性 和行为 this.name = name this.age = age this.sex = sex // 3. 返回this // return this; } var p = new Person()
var obj = {name: "张三"}
console.log('obj.toString------', obj.toString()) // [object Object]
var arr = [1, 2, 3]
console.log('arr.toString()------', arr.toString()) // 1,2,3
in:检测某一个属性是否属于这个对象(attr in object),不管是私有的属性还是公有的属性,只有存在用in来检测都是true
// console.log(''getX'' in f1) // true 是它的一个属性
hasOwnProperty:用来检测某一个属性是否为这个对象的私有属性,这个方法只能检测私有的属性
(思考:检测一个属性是否为该对象的 '' 公有属性 '')
function hasPubProperty(attr, obj){
// 首先保证是它的一个属性并且还不是私有的属性,那么只能是公有的属性了
return (attr in obj) && (!obj.hasOwnProperty(attr))
}