js面向对象
一.js类的生成方式
1.工厂模式
function Person(options) {
var obj = new Object();
obj.name = options.name;
obj.age = options.age;
obj.say = function () { console.log("工厂模式"); }
return obj;
}
var person = Person({ name: 'A', age: 18 });/* 没有new */
console.log(person);/* Object{name: "A", age: 18, say: ƒ} */
person.say();/* 工厂模式 */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
缺点:构造函数都为Object,不同类之间区分不方便。
2.构造函数模式
function Person2(options) {
this.name = options.name;
this.age = options.age;
this.say = function () {
console.log("构造函数");
}
}
var person2 = new Person2({ name: 'B', age: 16 });
console.log(person2);/* Person2 {name: "B", age: 16, say: ƒ} */
person2.say();/* 构造函数 */
console.log(person2 instanceof Person2);/* true */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
缺点:创建每个实例都要创建相同方法,浪费内存。
3.原型模式
function Person3(options) {
this.name = options.name;
this.age = options.age;
}
Person3.prototype.say = function () {
console.log("原型 " + this.name);
}
var person3 = new Person3({ name: 'C', age: 21 });
console.log(person3);/* Person3 {name: "C", age: 21} */
person3.say();/* 原型 C */
console.log(person3.__proto__);/* say: ƒ () constructor: ƒ Person3(options) */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
原型模式不仅可以防止命名冲突,还可以实现所有实例共用prototype的内容,当函数以构造函数形式调用,所创建的对象会有一个隐含属性__proto__指向该构造函数的原型对象,prototype,proto,constructor的关系如下图:
当ldh对象实例使用一个本身没有的属性或方法时,会利用__proto__一直往上延申寻找直到null,也就是Object._proto_。
二.继承
绑定this指向执行父类构造函数
优点:能继承父类私有属性
缺点:不能继承父类原型上的方法
/* 构造函数继承 */
function Parent() {
this.name = "zhangsan";
this.age = 18;
}
Parent.prototype.say = function () {
console.log(this.name);
}
function Son() {
Parent.call(this);
this.sex = "male";
}
let son = new Son();
console.log(son);//Son { name: 'zhangsan', age: 18, sex: 'male' }
// son.say();//报错
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
将子类原型绑定到一个父类的实例中实现继承
优点:父类原型的方法能让子类实例对象继承
缺点:子类实例修改父类引用属性会引起所有子类实例的属性改变
/* 原型链继承 */
function Parent() {
this.name = "zhangsan";
this.age = 18;
this.hobbies = ['music', 'painting'];
}
Parent.prototype.say = function () {
console.log(this.name);
}
function Son() {
this.sex = "male";
}
Son.prototype = new Parent();
let son = new Son();
let son2 = new Son();
console.log(son);//Parent { sex: 'male' }
son.say();//zhangsan
console.log(son.hobbies);//[ 'music', 'painting' ]
//son改变引起son2改变
son.hobbies.push('reading');
console.log(son.hobbies);//[ 'music', 'painting', 'reading' ]
console.log(son2.hobbies);//[ 'music', 'painting', 'reading' ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
借用构造函数继承属性:在Son构造函数中改变通过改变this指向并传参的方式调用Father实现继承属性。
利用原型继承方法:将Father的实例对象作为Son的原型,并且把Son原型的constructor强制改为Son,Son可以通过__proto__访问父类的方法,实现过程如下图:
注意:不能直接Son.prototype=Father.prototype(因为这种情况下,子类和父类指向同一原型对象,子类独有的方法父类也会拥有,例如父类是不能有exam方法的)。
function Father(name, age) {
this.name = name;
this.age = age;
}
Father.prototype.money = function () {
console.log(100000);
}
function Son(name, age) {
// 借用父类构造函数,改变this指向实现继承属性
Father.call(this, name, age);
}
/* 将Father的一个实例作为Son的原型 */
Son.prototype = new Father();//或者Object.create(Father.prototype);
/* Father实例的constructor是Father,需要强制改为Son */
Son.prototype.constructor = Son;
//Son自己的方法
Son.prototype.exam = function () {
console.log("100分");
}
console.log(new Son('Mike', 11));/* Son {name: "Mike", age: 11} */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
与其它语言类似,直接在定义时加关键字,注意的是构造函数里面需要用super传参并且super必须在最前面。
class Father {
constructor(name, age) {
this.name = name;
this.age = age;
}
showInfo() { console.log(this.name, this.age); }
}
class Son extends Father {
constructor(name, age) {
super(name, age);//传入父类
this.name = name;
this.age = age;
}
}
var son = new Son('zhangsan', 22);
console.log(son);/* Son {name: "zhangsan", age: 22} */
son.showInfo();/* zhangsan 22 */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
三.重载
与其他语言不同,js中的重载并不是真正意义上的重载,而是在一个方法内根据传参情况不同做出不同表现。
function count(){
if(arguments.length%2==1){
console.log( "奇数个参数");
}else{
console.log("偶数个参数");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
四.重写
继承了父类,但是自己原型上定义了同名方法,在调用时执行自己原型上的方法。
function Father(name, age) {
this.name = name;
this.age = age;
}
Father.prototype.money = function () {
console.log(100000);
}
function Son(name, age) {
// 借用父类构造函数,改变this指向实现继承属性
Father.call(this, name, age);
}
/* 将Father的一个实例作为Son的原型 */
Son.prototype = new Father();
/* Father实例的constructor是Father,需要强制改为Son */
Son.prototype.constructor = Son;
/* Son重写方法 */
Son.prototype.money = function () {
console.log(100);
}
var son = new Son('Mike', 11);
son.money();//100
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
五.多态
多态的概念是对同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。
function Dog() { };
function Cat() { };
Dog.prototype.sound = function () { console.log("汪!") }
Cat.prototype.sound = function () { console.log('喵!') };
function makeSound(animal) {
animal.sound();
}
makeSound(new Dog());//汪!
makeSound(new Cat());//喵!