类和实例是大多数面向对象编程语言的基本概念,js通过原型(prototype)来实现面向对象编程,
通过new(语法糖)类得到实例,实例的原型就是类,实例一定具有类的属性和方法,但类不具有实例特有的属性和方法。
之所以使用原型的概念,因为这样可以让多个实例相同的属性方法都指向同一个地址,可以节省内存
ES5通过构造函数创建类,再通过prototype给类添加属性和方法
ES6通过class创建类,在constructor() 里写自身特有的方法属性,在外面写公有方法属性,
私有公有指的是其继承类能否拥有其属性方法,私有公有的属性方法都可以被实例化的对象拥有
创建类并实例化
Pep = function (name) {
this.name = name
}
Pep.prototype.bb = function (msg) {
console.log(this.name + ' bb:' + msg)
}
var ming = new Pep('xiao ming')
ming.bb('I can')// xiao ming bb:I can
class Animal { //class定义类
//构造方法(相当于es5的构造函数)内定义自己私有的属性方法,无法被子类继承
constructor(type) {
this.type = type
}
//方法外(相当于利用protype)定义公有的属性方法,可以被子类继承
says(say) {
console.log(this.type + ' says ' + say)
}
}
var animal = new Animal('cat') //创建实例对象
animal.says('hi') //cat says hi 实例对象继承其原型对象的属性方法
new
定义:
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)
// 构造器函数
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
// 1.以构造器的prototype属性为原型,创建新对象;
let child = Object.create(Parent.prototype);
// 2.将this和调用参数传给构造器执行
Parent.apply(child, rest);
// 3.返回第一步的对象
return child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';
//最后检验,与使用new的效果相同
child instanceof Parent//true
child.hasOwnProperty('name')//true
child.hasOwnProperty('age')//true
child.hasOwnProperty('sayName')//false
继承
// Father - 父类(FatherClass)
function Father() {
this.x = 0;
this.y = 0;
}
// 父类的方法
Father.prototype.move = function (x, y) {
this.x += x;
this.y += y;
console.info('Father moved.');
};
//1.定义子类并复制父类自身的属性和方法
function Son() {
Father.call(this);
}
//tips:call用来重定义(函数内部)this 这个对象的
//2.子类复制父类原型对象
Son.prototype = Object.create(Father.prototype);
//ps:
//Object.create方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
//prototype 属性允许您向对象添加属性和方法,Prototype 是全局属性,适用于所有的 Javascript 对象
//主要用于自定义的类和js内置对象如String、Date、Array等
//所有继承和实例化后的类和实例都拥有父类和构造函数的属性和方法
//constructor 属性返回对象的构造函数,返回值是函数的引用,不是函数名:
//JavaScript 数组 constructor 属性返回 function Array() { [native code] }
//必须创建一个新对象,不然会赋值只是浅拷贝,改变子类原型会影响到父类原型
//3.改变prototype后,constructor也改变了,所以要修复一下
Son.prototype.constructor = Son;
var obj = new Son();
console.log('Is obj an instance of Son?', obj instanceof Son); // true
console.log('Is obj an instance of Father?', obj instanceof Father); // true
console.log(obj.__proto__===Son.prototype); // true
console.log(obj.__proto__.__proto__===Son.prototype.__proto__);// true
//.__proto__属性是Object.prototype (en-US) 一个简单的访问器属性,其中包含了get(获取)和set(设置)的方法,任何一个__proto__的存取属性都继承于Object.prototype
obj.move(1, 1); // Outputs, 'Father moved.'
// 如果你希望能继承到多个对象,则可以使用混入的方式
function sonClass() {
FatherClass.call(this);
OtherFatherClass.call(this);
}
// 继承一个类
sonClass.prototype = Object.create(FatherClass.prototype);
// 混合其它
Object.assign(sonClass.prototype, OtherFatherClass.prototype);
// 重新指定constructor
sonClass.prototype.constructor = sonClass;
sonClass.prototype.myMethod = function () {
// do a thing
};
extends:extends
关键字用来创建一个普通类或者内建对象的子类。
class myDate extends Date {
constructor() {
super();
}
getFormattedDate() {
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
return this.getDate() + "-" + months[this.getMonth()] + "-" + this.getFullYear();
}
}
Object.setPrototypeOf() 方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null。()
var Father = {
// ...
}
class Child extends Father {
// ...
}
// Uncaught TypeError: Class extends value #<Object> is not a constructor or null
// 解决方案
Object.setPrototypeOf(Child.prototype, Father);
1.原型链继承
Dog.prototype = new Animal(); //将Animal的实例挂载到了Dog的原型链上
2.构造继承
function Cat(name) { Animal.call(this); this.name = name || 'Tom'; }
3.寄生组合继承
//核心:通过寄生方式,砍掉父类的实例属性,这样,在调用俩次父类的构造的时候,就不会初始化俩次实例方法/属性,避免了组合继承的缺点。
function Cat(name) {
Animal.call(this);
this.name = name || 'Tom';
}
(function() {
var Super = function() {}; //创建一个没有实例的方法类。
Super.prototype = Animal.prototype;
Cat.prototype = new Super(); //将实例作为子类的原型。
})();
let cat = new Cat();
console.log(cat.name); //Tom
cat.sleep(); //Tom正在睡觉
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
Cat.prototype.constructor = Cat; //修复构造函数