JS中的类很难吗?

什么是Class 类?

MDN上说:类定义对象的特征。它是对象的属性和方法的模板定义。
简单说,“类”是生产对象的模板,通过类这个模板,可以毫不费劲地生产出无数个一样的对象,而不用通过一次次的定义去声明对象。而这些对象,因为具有一样的属性、一样的方法,所以将这些对象归为一个“类”,就像将人类归入人这一类一样。

JavaScript 的类

在es 6 出现之前,ECMAScript 标准中都是没有类的官方规范的,JavaScript 的类都是通过其他的方法来模拟定义。直到ES 6 标准的到来,JavaScript 才拥有官方的定义类的方法。

定义类的方法

1. 构造函数法

构造函数法使用构造函数来模拟“类”,使用 this 在构造函数内部指代实例对象。

function Person(){
    this.species = 'human'
}

定义一个构造函数之后,使用 new 关键字来生成实例对象

let xxx = new Person
console.log(xxx)     // {species: 'human'}

像上面定义的构造函数 Person,可以使用 new 关键字来生成无数个 拥有属性 species = 'human' 的对象。
然而,使用这种方法构造对象,当构造对象的数量太多时,会极大地消耗内存,所以JavaScript提供了函数的prototype属性来节约内存。

function Person(){

}

Person.prototype.species = 'human'

let xxx = new Person()
console.log(xxx)   // {}

可以看到,使用 Person 的prototype属性定义对象的公共属性 species,依然可以生成一个对象。然而生成的却是一个空对象。那么species属性去哪了?
species属性跑到了实例对象 xxx 的原型上了:

console.log(xxx.species)      // human
console.log(xxx.__proto__)    // {species:human,constructor:function}

通过构造函数的prototype属性,可以将实例对象的公共属性集成到一个原型对象上面,节约内存

构造函数法同时还可以实现对象的自有属性和自有方法

function Person(){

}

Person.prototype.species = 'human'

let xxx = new Person()
xxx.abc = "abc"
console.log(xxx)   // {abc:abc}

构造函数通过将值以参数的形式传入函数内部,使构造出的实例对象具有不同的属性值。

function Person(name,age){
    this.name = name
    this.age = age
}

Person.prototype.species = 'human'

let xxx = new Person(‘xiao’,18)
console.log(xxx)   // {name:xiao,age:18}

那么,使用构造函数法模拟类的流程是:

function 构造函数名(自有属性值1,自有属性值2,...){
    this.自有属性1 = 自有属性值1
    this.自有属性2 = 自有属性值2
}

构造函数名.prototype.xxx = xxx  // 设置构造函数的原型属性
// 还可以直接往实例对象上添加自己的属性

2. Object.create() 实现类

Object.create()语法

Object.create(proto[, propertiesObject])
参数:

  • proto:新创建对象的原型对象
  • propertiesObject:新创建对象的属性配置。(如:是否可枚举、是否只写等)
    返回值:
  • 返回新创建的对象。

使用Object.create()模拟类,是将一个对象直接作为新创建对象的原型,直接将原型植入新对象。
在这种方法中,“类”就是一个对象,而不是函数。

let Person = {
    species: 'human',
    walk: function(){},
    speak: function(){},
}

let xxx = Object.create(Person)
console.log(xxx)

上面这段代码,以 Person 这个对象作为原型,生成一个新的空对象 xxx,xxx 的原型指向 Person。换言之,对象 Person 被当做了一个类,创建新的对象。

Object.create()模拟类的缺陷:

  • 实例对象的属性全部在同一个”类“对象上面,只能实例对象名.属性名 = 属性值手动添加自有属性和自有方法
  • 由于Object.create() 只是将创建的实例对象的原型绑定到一个”类“对象上面。一旦”类“对象发生改变,所有的实例对象的值都会改变。
  • 实例对象的共享数据全部绑定在”类“对象上面。

3. 极简主义法

极简主义法同样使用一个对象作为”类“,在对象里面,定义一个createNew方法来生成实例

let Person = {
    createNew: function(){},
}

createNew方法里面,定义一个实例对象作为返回值

let Person = {
    createNew: function(){
        let person = {}
        person.species = "human"
        person.walk = function(){}
        person.speak = function(){}
        return person
    },
}

调用createNew方法,就可以得到一个新的对象

let xxx = Person.createNew()
console.log(xxx)  // {species: "human", walk: function, speak: function}

极简主义法的原理:使用一个对象作为原本,去复制完成另一个对象
事实上,极简主义法的原理概念与Object.create()极为类似,两个的唯一区别是:极简主义法不会修改实例对象的原型,而Object.create()涉及到原型。两者之间的公共属性共享全部是通过操作“原本”来实现。

4. ES 6 的 class 声明

ES 6 的 class 不是一个全新的类继承模型,而是一个原有模型的语法糖。
ECMAScript2015 将 第一种:构造函数法 给官方化,定义一个 api 直接使用“类”。本质上, class 定义的“类”还是一个函数

class Person {
    constructor(name, age){
        this.name = name
        this.age = age
    }
    walk(){}
    speak(){}
}

let xxx = new Person('xiao',18)
typeof Person     // "function",Person 本质上还是一个函数
console.log(xxx)   // {name: "xiao", age: 18}

用函数模拟一个类的过程(举例)

假设现在在设计一款游戏,需要生成许多小兵,就需要一个生成小兵的类。
使用函数来生成小兵

function createBing(id,hp){
    let bing = {}    //  创建一个空对象存储小兵的属性
    bing.id = id
    bing.hp = hp
    bing.attack = 5
    bing.walk= function(){console.log('walk')}
    return bing
}

此时,调用函数 createBing 就能生成一个具有4个属性的小兵对象。
此时,生成数量多的小兵时,会重复创建 hp 和 walk 这两个属性,浪费内存。JS 中有原型,可以将公共属性绑定到原型上面。

// 首先需要一个原型对象,将公共属性放到原型对象上面
bingPrototype = {
    attack: 5,
    walk: function(){console.log('walk')}
}

function creareBing(id, hp){
    let bing = {}

    bing.__proto__ = bingPrototype // 将原型属性绑定到生成的对象上面

    bing.id = id
    bing.hp = hp

    return bing
}

此时,调用 createBing 函数可以生成一个具有 id 和 hp 两个属性的小兵对象,attack 和 walk 被绑定到原型上面,所有小兵对象共享。

由于__proto__不是标准规范,所以使用另一个符合规范的方法,使用函数的 prototype 属性和new关键字。
将实例对象的全部共有属性绑定到生成实例对象的函数的prototype属性上面,再用new关键字生成实例,可以直接将原型绑定到实例对象上。

function createBing(id, hp){
    this.id = id 
    this.hp = hp
}

createBing.prototype = {
    construcotr: createBing, // constrctor 是 prototype 的默认属性,此写法会覆盖,所以要重新赋值
    attack: 5,
    walk: function(){console.log('walk')}
}

至此,利用函数的prototypenew关键字,实现了用函数模拟类的目的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  •   面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意...
    霜天晓阅读 2,092评论 0 6
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,521评论 0 5
  • 终于,第一次在没有司机的陪伴下。我自己和一个孕妈妈上路了! 然后,嗯,其实就独自开了2公里,但是我最终还是牛哒哒的...
    笑莉说阅读 150评论 2 4
  • 我对济南,可谓是第二故乡,除了自小生活的家乡外,这里承载我四年的记忆,还有一群同学和朋友,虽然我没有老朋友,老同学...
    yariel阅读 232评论 0 2
  • Considerations for Styling a Modal 原文地址modal,dialog,弹出层,会...
    Quilljou阅读 495评论 0 0