JS对象创建与继承的汇总梳理

目录
  • 引言
  • 对象创建
  • 工厂函数
  • 构造函数
  • 构造+原型
  • 工厂+构造+原型
  • ES6 class
  • 对象与函数
  • 小结

引言

在 6 月更文中零零散散讲了 JS 的对象创建和对象继承,有工友对此还是表示疑惑,要注意:这是两个不同但又相关的东西,千万别搞混了!

这些文章是:

蓦然回首,“工厂、构造、原型”设计模式,正在灯火阑珊处

JS精粹,原型链继承和构造函数继承的 “毛病”

“工厂、构造、原型” 设计模式与 JS 继承

JS 高级程序设计 4:class 继承的重点

JS class 并不只是简单的语法糖!

本篇作为汇总篇,来一探究竟!!冲冲冲

对象创建

不难发现,每一篇都离不开工厂、构造、原型这 3 种设计模式中的至少其一!

让人不禁想问:JS 为什么非要用到这种 3 种设计模式了呢??

正本溯源,先从对象创建讲起:

我们本来习惯这样声明对象(不用任何设计模式)

let car= {
        price:100,
        color:"white",
        run:()=>{console.log("run fast")}
}

当有两个或多个这样的对象需要声明时,是不可能一直复制写下去的:

let car1 = {
        price:100,
        color:"white",
        run:()=>{console.log("run fast")}
}
let car2 = {
        price:200,
        color:"balck",
        run:()=>{console.log("run slow")}
}
let car3 = {
        price:300,
        color:"red",
        run:()=>{console.log("broken")}
}

这样写:

  • 写起来麻烦,重复的代码量大;
  • 不利于修改,比如当 car 对象要增删改一个属性,需要多处进行增删改;

工厂函数

肯定是要封装啦,第一个反应,可以 借助函数 来帮助我们批量创建对象~

于是乎:

function makeCar(price,color,performance){
        let obj = {}
        obj.price = price
        obj.color= color
        obj.run = ()=>{console.log(performance)}
        return obj
}
let car1= makeCar("100","white","run fast")
let car2= makeCar("200","black","run slow")
let car3= makeCar("300","red","broken")

这就是工厂设计模式在 JS 创建对象时应用的由来~

到这里,对于【对象创建】来说,应该够用了吧?是,在不考虑扩展的情况下,基本够用了。

但这个时候来个新需求,需要创建 car4、car5、car6 对象,它们要在原有基础上再新增一个 brand 属性,会怎么写?

第一反应,直接修改 makeCar

function makeCar(price,color,performance,brand){
        let obj = {}
        obj.price = price
        obj.color= color
        obj.run = ()=>{console.log(performance)}
        obj.brand = brand
        return obj
}
let car4= makeCar("400","white","run fast","benz")
let car5= makeCar("500","black","run slow","audi")
let car6= makeCar("600","red","broken","tsl")

这样写,不行,会影响原有的 car1、car2、car3 对象;

那再重新写一个 makeCarChild 工厂函数行不行?

function makeCarChild (price,color,performance,brand){
        let obj = {}
        obj.price = price
        obj.color= color
        obj.run = ()=>{console.log(performance)}
        obj.brand = brand
        return obj
}
let car4= makeCarChild("400","white","run fast","benz")
let car5= makeCarChild("500","black","run slow","audi")
let car6= makeCarChild("600","red","broken","tsl")

行是行,就是太麻烦,全量复制之前的属性,建立 N 个相像的工厂,显得太蠢了。。。

构造函数

于是乎,在工厂设计模式上,发展出了:构造函数设计模式,来解决以上复用(也就是继承)的问题。

function MakeCar(price,color,performance){
        this.price = price
        this.color= color
        this.run = ()=>{console.log(performance)}
}
function MakeCarChild(brand,...args){
        MakeCar.call(this,...args)
        this.brand = brand
}
let car4= new MakeCarChild("benz","400","white","run fast")
let car5= new MakeCarChild("audi","500","black","run slow")
let car6= new MakeCarChild("tsl","600","red","broken")

构造函数区别于工厂函数:

  • 函数名首字母通常大写;
  • 创建对象的时候要用到 new 关键字(new 的过程这里不再赘述了,之前文章有);
  • 函数没有 return,而是通过 this 绑定来实现寻找属性的;

到此为止,工厂函数的复用也解决了。

构造+原型

新的问题在于,我们不能通过查找原型链从 MakeCarChild 找到 MakeCar

car4.__proto__===MakeCarChild.prototype // true
MakeCarChild.prototype.__proto__ === MakeCar.prototype // false
MakeCarChild.__proto__ === MakeCar.prototype // false

无论在原型链上怎么找,都无法从 MakeCarChild 找到 MakeCar

这就意味着:子类不能继承父类原型上的属性

这里提个思考问题:为什么“要从原型链查找到”很重要?为什么“子类要继承父类原型上的属性”?就靠 this 绑定来找不行吗?

于是乎,构造函数设计模式 + 原型设计模式 的 【组合继承】应运而生

function MakeCar(price,color,performance){
        this.price = price
        this.color= color
        this.run = ()=>{console.log(performance)}
}
function MakeCarChild(brand,...args){
        MakeCar.call(this,...args)
        this.brand = brand
}
MakeCarChild.prototype = new MakeCar() // 原型继承父类的构造器
MakeCarChild.prototype.constructor = MakeCarChild // 重置 constructor
let car4= new MakeCarChild("benz","400","white","run fast")

现在再找原型,就找的到啦:

car4.__proto__ === MakeCarChild.prototype // true
MakeCarChild.prototype.__proto__ === MakeCar.prototype // true

其实,能到这里,就已经很很优秀了,该有的都有了,写法也不算是很复杂。

工厂+构造+原型

但,总有人在追求极致。

上述的组合继承,父类构造函数被调用了两次,一次是 call 的过程,一次是原型继承 new 的过程,如果每次实例化,都重复调用,肯定是不可取的,怎样避免?

工厂 + 构造 + 原型 = 寄生组合继承 应运而生

核心是,通过工厂函数新建一个中间商 F( ),复制了一份父类的原型对象,再赋给子类的原型;

function object(o) { // 工厂函数
  function F() {}
  F.prototype = o;
  return new F(); // new 一个空的函数,所占内存很小
}
function inherit(child, parent) { // 原型继承
  var prototype = object(parent.prototype)
  prototype.constructor = child
  child.prototype = prototype
}
function MakeCar(price,color,performance){
        this.price = price
        this.color= color
        this.run = ()=>{console.log(performance)}
}
function MakeCarChild(brand,...args){  // 构造函数
        MakeCar.call(this,...args)
        this.brand = brand
}
inherit(MakeCarChild,MakeCar)
let car4= new MakeCarChild("benz","400","white","run fast")
car4.__proto__ === MakeCarChild.prototype // true
MakeCarChild.prototype.__proto__ === MakeCar.prototype // true

ES6 class

再到后来,ES6 的 class 作为寄生组合继承的语法糖:

class MakeCar {
	constructor(price,color,performance){
			this.price = price
			this.color= color
			this.performance=performance
	}
	run(){
		console.log(console.log(this.performance))
	}
}
class MakeCarChild extends MakeCar{
    constructor(brand,...args){
        super(brand,...args);
        this.brand= brand;
    }
}
let car4= new MakeCarChild("benz","400","white","run fast")
car4.__proto__ === MakeCarChild.prototype // true
MakeCarChild.prototype.__proto__ === MakeCar.prototype // true

有兴趣的工友,可以看下 ES6 解析成 ES5 的代码:原型与原型链 - ES6 Class的底层实现原理 #22

对象与函数

最后本瓜想再谈谈关于 JS 对象和函数的关系:

即使是这样声明一个对象,let obj = {} ,它一样是由构造函数 Object 构造而来的:

let obj = {}
obj.__proto__ === Object.prototype // true

在 JS 中,万物皆对象,对象都是有函数构造而来,函数本身也是对象。

对应代码中的意思:

  • 所有的构造函数的隐式原型都等于 Function 的显示原型,函数都是由 Function 构造而来,Object 构造函数也不例外;
  • 所有构造函数的显示原型的隐式原型,都等于 Object 的显示原型,Function 也不例外;
// 1.
Object.__proto__ === Function.prototype // true
// 2.
Function.prototype.__proto__ === Object.prototype // true

这个设计真的就一个大无语,大纠结,大麻烦。。。

只能先按之前提过的歪理解记着先:Function 就是上帝,上帝创造了万物;Object 就是万物。万物由上帝创造(对象由函数构造而来),上帝本身也属于一种物质(函数本身却也是对象);

对于本篇来说,继承,其实都是父子构造函数在继承,然后再由构造函数实例化对象,以此来实现对象的继承。

到底是谁在继承?函数?对象?都是吧~~

小结

本篇由创建对象说起,讲了工厂函数,它可以做一层最基本的封装;

再到,对工厂的拓展,演进为构造函数;

再基于原型特点,构造+原型,得出组合继承;

再追求极致,讲到寄生组合;

再讲到简化书写的 Es6 class ;

以及最后对对象与函数的思考。

更多关于JS对象创建继承的资料请关注我们其它相关文章!

(0)

相关推荐

  • javascript的函数、创建对象、封装、属性和方法、继承

    一,function 从一开始接触到js就感觉好灵活,每个人的写法都不一样,比如一个function就有N种写法 如:function showMsg(){},var showMsg=function(){},showMsg=function(){} 似乎没有什么区别,都是一样的嘛,真的是一样的吗,大家看看下面的例子 复制代码 代码如下: ///----------------------------------------------------------------------------

  • JavaScript对象创建及继承原理实例解剖

    对象创建: 当一个函数对象被创建时候,Function构造器产生的函数对象会运行类似这样的代码: 复制代码 代码如下: this.prototype={constructor:this}; 假设函数F F用new方式构造对象时,对象的constructor被设置成这个F.prototype.constructor 如果函数在创建对象前修改了函数的prototype,会影响创建出来对象的construtor属性 如: 复制代码 代码如下: function F(){}; F.prototype={

  • 浅谈js对象的创建和对6种继承模式的理解和遐想

    JS中总共有六种继承模式,包括原型链.借用构造函数.组合继承.原型式继承寄生式继承和寄生组合式继承.为了便于理解记忆,我遐想了一个过程,对6中模式进行了简单的阐述. 很长的一个故事,姑且起个名字叫女娲造人吧. 创建对象 女娲一个一个的捏人(创建对象),这样太慢,于是设计了一种机器(函数),想造什么样的,告诉他这个人有哪些特点和功能,机器来制造.这就是工厂模式的(使用同一个接口创建对象,回产生大量重复代码,由此发明了一种函数(模具)). 但是机器造人同样也比较麻烦(挖土.和泥.捏眼睛.捏鼻子...

  • JavaScript中创建对象和继承示例解读

    对象创建: 当一个函数对象被创建时候,Function构造器产生的函数对象会运行类似这样的代码: 复制代码 代码如下: this.prototype={constructor:this}; 假设函数F F用new方式构造对象时,对象的constructor被设置成这个F.prototype.constructor 如果函数在创建对象前修改了函数的prototype,会影响创建出来对象的construtor属性 如: 复制代码 代码如下: function F(){}; F.prototype={

  • 详解js创建对象的几种方法及继承

    创建对象 通过Object构造函数或对象字面量创建单个对象 这些方式有明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码.为了解决这个问题,出现了工厂模式. 工厂模式 考虑在ES中无法创建类(ES6前),开发人员发明了一种函数,用函数来封装以特定接口创建对象的细节.(实现起来是在一个函数内创建好对象,然后把对象返回). function createPerson(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.j

  • javascript创建对象、对象继承的实用方式详解

    本文约定:不特殊声明的情况下,属性代指属性或方法. 创建对象.对象继承实际上是一回事:我们所需要的实例对象通过构造函数获得私有属性.通过原型链获得共享的属性.什么是好的方式?私有属性通过构造函数的方式获得(不考虑实例中自定义私有属性)且不需要重写,共享属性通过原型链找到且不需要重复创建. 普适的方式 组合使用构造函数模式和原型模式创建对象 function HNU_student(name) { this.name = name; this.sayName = function() { retu

  • JS对象创建与继承的汇总梳理

    目录 引言 对象创建 工厂函数 构造函数 构造+原型 工厂+构造+原型 ES6 class 对象与函数 小结 引言 在 6 月更文中零零散散讲了 JS 的对象创建和对象继承,有工友对此还是表示疑惑,要注意:这是两个不同但又相关的东西,千万别搞混了! 这些文章是: 蓦然回首,“工厂.构造.原型”设计模式,正在灯火阑珊处 JS精粹,原型链继承和构造函数继承的 “毛病” “工厂.构造.原型” 设计模式与 JS 继承 JS 高级程序设计 4:class 继承的重点 JS class 并不只是简单的语法糖

  • 基于JS对象创建常用方式及原理分析

    前言 俗话说"在js语言中,一切都对象",而且创建对象的方式也有很多种,所以今天我们做一下梳理 最简单的方式 JavaScript创建对象最简单的方式是:对象字面量形式或使用Object构造函数 对象字面量形式 var person = new Object(); person.name = "jack"; person.sayName = function () { alert(this.name) } 使用Object构造函数 var person = { na

  • js对象的复制继承实例

    本文实例讲述了js对象的复制继承.分享给大家供大家参考.具体如下: 复制代码 代码如下: <script type="text/javascript"> Object.prototype.extend = function(obj){  //在函数里,把obj属性复制到自身  for(var k in obj){   if(obj.hasOwnProperty(k)){    if(this[k] == undefined){     this[k] = obj[k];  

  • JS对象创建的几种方式整理

    最近一直在看JS高级程序设计这本书,有空来梳理一下几种创建对象的方式.话不多说,直接步入正题. 第一种:Object构造函数创建 var Person = new Object(); Person.name = 'Nike'; Person.age = 29; 这行代码创建了Object引用类型的一个新实例,然后把实例保存在变量Person中. 第二种:使用对象字面量表示法 var Person = {};//相当于var Person = new Object(); var Person =

  • jQuery高级编程之js对象、json与ajax用法实例分析

    本文实例讲述了jQuery高级编程之js对象.json与ajax用法.分享给大家供大家参考,具体如下: js对象 创建js对象的两种方式: 1.通过new Object创建: var p1 = new Object(); // 设置属性,和方法 p1.name = "张三"; p1.age = 13; p1.study = function(){ console.log(p1.name + "正在学习,葵花宝典"); } // 调用属性和方法 console.log

  • js对象的构造和继承实现代码

    复制代码 代码如下: <script> //定义js的user对象 function User(name,age){ this.name=name, this.age=age, this.getName=function(){ return this.name; }, this.getAge=function(){ return this.age; } } //实例化一个对象 var use=new User("aa",21); alert(use.name); alert

  • JS自定义对象创建与简单使用方法示例

    本文实例讲述了JS自定义对象创建与简单使用方法.分享给大家供大家参考,具体如下: <html> <head> <title>js自定义函数</title> <meta charset="UTF-8"/> <script type="text/javascript"> //创建自定义对象 //方法一: // var obj=new Object(); //方法二 var obj={}; obj.n

  • JavaScript必知必会(七)js对象继承

    对象继承inherit var o = { r: }; var c = function f() { }; c.prototype = o; c.r = ; alert(o.r);//被继承的属性值未发生改变.alert(c.r);//c中r覆盖了o中的属性. 如何调用o中的r属性呢. var o = { r: }; var c = function f() { }; c.prototype = o; alert(o.r);// 被继承的属性值未发生改变. alert(c.r);//查询r属性,

随机推荐