创建对象和实现原型继承的几种方式

对象的属性类型

  1. 数据属性:使用以下四个值来描述行为特性。
  • Configurable(true):表示能否通过delete来删除属性,能否修改属性值,能否设置为访问器属性
  • Enumerable(true):他能够否通过for…in循环访问属性
  • Writable(true):能否修改属性值
  • Value:数据值
  1. 访问器属性:
  • hasOwnProperty()

如何判断属性存在位置?

  • 判断是存在于原型还是实例?hasOwnProperty(),实例中是true
  • 判断属性存在于对象中还是存在于原型中?in操作符与hasOwnProperty()
!object.hasOwnProperty(name) && (name in object12)
true:  原型    false:  实例

创建对象

1.工厂模式
function createPerson(name, age, job){
	let o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;
	o.sayName = function(){
		console.log("hello, ", this.name);
	}
	return o;
}
let person1 = createPerson("Saury", "14", "IT工");
  • 对象无法识别
2.构造函数模式
function Person(name, age, job){ //构造函数名字应该大写字母开头
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = function(){
		console.log("hello, ", this.name);
	}
}
let person1 = new Person("Saury", "14", "IT工");
let person1 = new Person("Saury", "14", "IT工");
// false,是两个不同的对象
console.log(person1.sayName == person2.sayName); 
  • 将构造函数当做普通的函数使用,Person("...");,此时属性和方法都被添加到window
  • 每个实例的方法都不相同,实则完成的是同样的功能,所以会造成浪费,因此可以使用以下的方式(但是该方法会导致构造函数封装性差)
function Person(name, age, job){ //构造函数名字应该大写字母开头
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = sayName;
}
function sayName(){
	console.log("hello, ", this.name);
} 
3.原型模式
function Person(name, age, job){
    Person.prototype.name = name;
    Person.prototype.age = age;
    Person.prototype.job = job;
    Person.prototype.sayName = function(){
        console.log("Hello", this.name);
    }
}
let person1 = new Person("Kirs1", "27", "singer1");
let person2 = new Person("Kirs2", "27", "singer2");
//true,由于这两个实例的方法都是挂在原型上的,所以指向的是同一个方法,则相同
console.log(person1.sayName == person2.sayName); 
4.组合使用构造函数和原型模式

构造函数用于定义实例属性,原型模式用于定义方法和共享的属性

?后三种不常用就不写了

5.动态构造函数模式
6.寄生构造函数模型
7.稳妥构造函数模式

实现继承

1.原型链继承
function Super(){
    this.supV = 'supV';
}
Super.prototype.getSuperValue = function(){
    return this.supV;
}
function Suber(){
    this.subV = 'subV';
}
//实现Suber继承Super
Suber.prototype = new Super();
Suber.prototype.getSuberValue = function(){
    return this.subV;
}
//创建Suber的实例调用Super的方法
let oo = new Suber();
console.log(oo.getSuperValue()); //'supV'

存在问题

  1. 原型的方法会被所有实例共享,造成实例间的属性会相互影响
  2. 在创建子类的实例时,不能向超类型的构造函数传递参数
2.借用构造函数

在子类的构造函数内部调用父类型构造函数

function Super(name){
    this.name = name;
}
function Suber(){
    Super.call(this, 'kirs');
    this.age = '27';
}
let oo = new Suber();
console.log(oo.name); //'kris'

存在问题

  1. 方法都定义在构造函数中,没有办法进行函数复用。父类的方法没有被共享,造成内存浪费。
  2. 在超类的原型上中定义的方法,对于子类是不可见的
3.组合继承

使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样使得定义在原型上的的方法实现函数复用,又能够保证每一个实例都有自己的属性。

function Super(name){
	this.name = name;
}
Super.prototype.sayName = function(){
	console.log(this.name);
}
function Suber(name, age){
	//继承属性
	Super.call(this, name); //第二次调用Super()
	this.age = age;
}
//继承实例的方法
Suber.prototype = new Super(); //第一次调用Super()
Suber.prototype.constructor = Suber;
Suber.prototype.sayAge = function(){
	sonsole.log(this.age);
}
//所有实例共有属性和方法:name, sayName()
//实例私有属性和方法:age, sayAge()

存在问题

  1. 组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一组在实例上,一组在SubType原型中。第一次调用super()时,会给Suber的原型上挂载name和age属性,当第二次调用时,又一次调用super(),会挂载到实例上,如下:
var a = new Suber();
console.log(Suber.prototype);
console.log(a);

在这里插入图片描述

4.es6的extends(语法糖,和寄生组合继承一样)
class Animal {
    constructor(name) {
        this.name = name
    } 
    getName() {
        return this.name
    }
}
class Dog extends Animal {
    constructor(name, age) {
        super(name)
        this.age = age
    }
}
5.原型式继承

该方法没有严格意义上的构造函数,借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends);   //"Shelby,Court,Van,Rob,Barbie"

也可以使用Object.create

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = Object.create(person, {name: {value: 'hah'}});
anotherPerson.friends.push("Rob");
console.log(anotherPerson.name); //hah

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.friends.push("Barbie");
console.log(yetAnotherPerson.name);   //Nicholas
6.寄生式继承
function createAnother(original){
    var clone = Object.create(original);    //通过调用函数创建一个新对象
    clone.sayHi = function(){           //以某种方式来增强这个对象
        console.log("hi");
    };
    return clone;                    //返回这个对象
}

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = createAnother(person);
anotherPerson.name = "jah";
console.log(anotherPerson.name); //"jah"
anotherPerson.sayHi(); //"hi" 

缺点

  1. 由于不能做到函数复用而降低效率(和构造函数类似)
7.寄生组合式继承

因为之前说组合继承会使得父类的构造函数被调用的俩次,所以该方法是用来解决这个问题的。通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

function inheritPrototype(subType, superType){
    var prototype = Object.create(superType.prototype);       //创建对象
    prototype.constructor = subType;                   //增强对象
    subType.prototype = prototype;                     //指定对象
}

这个示例中的inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加constructor属性,从而弥补因重写原型而失去的默认的constructor属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用inheritPrototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了

只调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。原型链还能保持不变;因此,还能够正常使用instanceof和isPrototypeOf()

es6(extends)和es5(寄生组合式继承)的继承有什么区别?

ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this)),然后再把原型链继承。
ES6的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法,才可使用this关键字,否则报错。),然后再用子类的构造函数修改this实现继承。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript中实现继承方式有以下几种: 1. 原型继承 优点:简单方便,易于理解和实现。 缺点:父类的引用属性会被多个实例共享,可能会出现意外修改;子类无法向父类构造函数传递参数。 示例代码: ```javascript function Animal() { this.species = 'animal'; } function Cat() {} Cat.prototype = new Animal(); ``` 2. 借用构造函数继承 优点:可以在子类构造函数中向父类构造函数传递参数;解决了父类引用属性被多个实例共享的问题。 缺点:无法实现函数的复用。 示例代码: ```javascript function Animal(name) { this.name = name; } function Cat(name) { Animal.call(this, name); } ``` 3. 组合继承 优点:综合了原型继承和借用构造函数继承的优点,既可以实现函数的复用,又可以向父类构造函数传递参数。 缺点:父类构造函数会被调用两次,造成了一些不必要的消耗。 示例代码: ```javascript function Animal(name) { this.name = name; } Animal.prototype.sayHello = function() { console.log('Hello, my name is ' + this.name); }; function Cat(name) { Animal.call(this, name); } Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; ``` 4. 原型继承 优点:简单方便,可以在不必创建构造函数的情况下实现继承。 缺点:引用属性会被共享,可能会造成意外修改。 示例代码: ```javascript function createObject(obj) { function F() {} F.prototype = obj; return new F(); } var animal = { species: 'animal' }; var cat = createObject(animal); ``` 5. 寄生式继承 优点:可以在不必创建构造函数的情况下实现继承,可以为对象添加专门的方法。 缺点:引用属性会被共享,可能会造成意外修改。 示例代码: ```javascript function createObject(obj) { var clone = Object.create(obj); clone.sayHello = function() { console.log('Hello, my name is ' + this.name); }; return clone; } var animal = { species: 'animal' }; var cat = createObject(animal); ``` 总体来说,不同的继承方式各有优缺点,应根据具体的需求来选择合适的方式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值