js面向对象(创建对象,原型及原型链,new,继承)

178 阅读2分钟

创建对象方式

工厂模式 无法正确识别出对象类型

function createObject(color, start){
    const obj = new Object();
    obj.color = color;
    obj.start = start;
}

createObject('white');
createObject('red');

构造函数模式

function createObject(color){
    this.color = color;
    this.start = function(){}
}
const white = createObject('white');
const black = createObject('black');
优点:改变对象的属性和方法,不会影响到其他对象;
缺点:this上挂载的对象都是面向对象的,当新建一个对象的时候,this上的属性都可以复制一份,浪费空间;

原型模式

function createObject(color){
    this.color = color;
}
createObject.prototype.start = function(){ console.log('原型上的方法');};
const white = createObject('white');
const black = createObject('black');
优点:start方法只会在内存中存一份,调用同一函数

静态属性

绑定在构造函数上的属性,通过构造函数访问

function createObject(color){
    this.color = color;
    if(!createObject.total){
        createObject.total = 0;
    }
    createObject.total++;
}
const white = createObject('white');
const black = createObject('black');
console.log(createObject.total);

原型链

原型链

function Player(color){
    this.color = color;
}
Player.prototype.start = function() {};
var white = new Player('white');
var black = new Player('black');
console.log(white.__proto__);
console.log(Object.getPrototypeOf(white));
console.log(Player.prototype);

原型链.png

屏幕快照 2022-01-10 下午3.36.03.png

new

new关键字做了什么

  • 一个继承Player.prototype的新对象white/black被创建
  • white._ proto_ 指向 Player.prototype
  • 将this指向新创建的对象
  • 返回这个对象
    • 如果构造函数没有显示的返回值,则返回this
    • 如果构造函数有显示的返回值,返回值是基本类型,则返回this
    • 如果构造函数有显示的返回值,返回值是对象类型,则返回这个对象,null除外。
function NewOption(){
    var obj = new Object();
    constructor = [].shift.call(arguments);
    obj.__proto__ = constructor.prototype
    var retrunObj = constructor.apply(obj,arguments);
    if(typeof returnObj === 'object' &&  returnObj !== null){
        return returnObj;
    }
    return obj;
}

继承

1、原型链继承

function Parent(){
    this.name = "parent"
}
Parent.prototype.getName = function (){
    console.log(this.name);
    return this.name;
}

function Child(){}
// 原型链继承
Child.prototype = new Parent();
Child.prototype.constructor = Child;

const child1 = new Child();
var child2 = new Child();
child1.getName();
隐含的问题:
1、如果属性是引用类型,一旦其中一个实例更改了其值,那么所有实例都会受影响。
2、创建child的时候无法传参。

2、构造函数继承

function Parent(name,actions){
    this.action = actions;
    this.name = name;
    this.eat = function(){
        console.log(`${name}-eat`)
    }
}

function Child(name, actions){
    Parent.call(this, name, actions);
}

const c1 = new Child('c1', ['eat']);
const c2 = new Child('c2', ['eat', 'sleep']);
c1.action.pop();
console.log(c1.action);
console.log(c2.action);
属性和方法被继承的话,只能在构造函数中定义
如果方法都在构造函数定义了,那么每次创建实例都会见一份,多占内存

3、组合继承

function Parent(name, actions){
    this.name = name;
    this.action = actions;
}
Parent.prototype.eat = function(){
    console.log(`${this.name}---eat`);
    console.log(`${this.action}-----`)
}
function Child(id){
    Parent.apply(this, Array.from(arguments).slice(1));
    this.id = id;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const c1 = new Child(1, 'c1', 'hahahha');
const c2 = new Child(2, 'c2', 'xixixixi');
console.log(c1.name);
console.log(c2.name);
c1.eat();
c2.eat();
 调用了2次构造函数Parent,做了重复操作

4、寄生组合继承

function Parent(name, actions){
    this.name = name;
    this.action = actions;
}
Parent.prototype.eat = function(){
    console.log(`${this.name}---eat`);
    console.log(`${this.action}-----`)
}
function Child(id){
    Parent.apply(this, Array.from(arguments).slice(1));
    this.id = id;
}
// Child.prototype = new Parent();
// 寄生式函数组合调用
// let tmpFunction = function() {}
// tmpFunction.prototype = Parent.prototype;
// Child.prototype = new tmpFunction();
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const c1 = new Child(1, 'c1', 'hahahha');
const c2 = new Child(2, 'c2', 'xixixixi');
console.log(c1.name);
console.log(c2.name);
c1.eat();
c2.eat();

5、class

 class Parent{
    constructor(name){
        this.name = name
    }
    eat(){
        console.log(`${this.name}----eat`);
    }
 }

 class Child extends Parent{
    constructor(name){
        super(...arguments);
    } 
 }

const c1 = new Child('c1');
const c2 = new Child('c2');
c1.eat();
c2.eat();