js的类详解

Javascript从当初的一个“弹窗语言”,一步步发展成为现在前后端通吃的庞然大物。javascript的受欢迎程度也是与日俱增,今年最受欢迎编程语言又是花落javascript,这实在是一个充满了活力的语言。如今,随随便一个网页 JS 代码量都不下数千行,要是node后端那更不止几千行了。(WTF)代码量的增加给编程带来的首要的问题就是如何去编写和维护如此庞大的代码呢?答案就是模块化思想,其实就是面向对象(OOP)编程,如今比较流行的三大前端框架(angular,react,vue)基本都实现了组件化编程,但组件化和我们所说的模块化又有所不同,应该可以这么理解,组件化是模块化的升级版,模块化是组件化的基础。那么,问题来了,javascript怎么面向对象编程呢?我想这很多老鸟也不定说的清楚吧,前端杂而活跃的各种社区,很多前端er一度依赖各种插件,根本不需要去了解这些深层的东西却照样能完成任务。但我想作为一个有追求的前端er还是很有必要去学习这些看似用不着的东西的,别的不说,就单单因为我们是前端er,只会用jquery一个稍微厉害的后端都能代替你。好吧,废话不多说,到底如何面向对象coding呢…..

很不幸,es5中并没有给出明确的定义「类」的概念,所以传统的面向对象编程似乎是行不通的,那么又该肿么办呢?值得庆幸的是,前辈们通过不断探索总结,成功的用javascript模拟出了「类」。那么,javascript的类又该怎么定义呢?

在面向对象编程中,类(class)是对象(object)的模板,定义了同一组对象(又称”实例”)共有的属性和方法。

正文

说到 JS 中的“类”就不得不说原型链和继承了,因为 JS 是没有真正意义上的类的,所谓的类就是基于原型链和继承来实现的,即使ES6中加入了class、extends关键字实现类和继承,但实际上还是基于原型链和继承, ES6 类(class)是 JavaScript 现有的原型继承的语法糖。

1. 原型链和继承

“ 在 javaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)。 ”

原型链

实际上在定义一个对象的时候原型链本身就已经生成了,javascript处处皆对象的思想放在这里理解起来就很容易了,看完后你会发现万物始于Object.prototype。那么我们都是如何定义一个对象的呢,博主总结的方法如下几个:

先初步来个demo具体解释下原型链是咋回事吧:

//someObject.[[Prototype]] 符号是用于指派 someObject 的原型。这个等同于 JavaScript 的 __proto__  属性(现已弃用)。。从 ES6 开始, [[Prototype]] 可以用Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问。这个方法可以放心使用博主亲测,主流浏览器已经支持了

// 假定有一个对象 o, 其自身的属性(own properties)有 a 和 b:

// {a: 1, b: 2}

// o 的原型 o.[[Prototype]]有属性 b 和 c:

// {b: 3, c: 4}

// 最后, o.[[Prototype]].[[Prototype]] 是 null.

// 这就是原型链的末尾,即 null,

// 根据定义,null 没有[[Prototype]].

// 综上,整个原型链如下:

// {a:1, b:2}---> {b:3, c:4} ---> null

来来来,撸袖子搞出个对象!!!

使用普通方法来创建一个对象

demo如下:

var o={

   a:0,

   b:function(){

       console.log(this.a)

   }

}

//创建一个对象

//原型链如下:

//o--->Object.prototype--->null

var a = ["yo", "whadup", "?"];

//创建一个数组对象

//(indexOf, forEach等方法都是从它继承而来).

//原型链如下:

//a--->Array.prototype ---> Object.prototype ---> null

function f(){

 return 1;

}

//创建一个函数对象

//(call, bind等方法都是从它继承而来):

//原型链如下:

//f ---> Function.prototype ---> Object.prototype ---> null

var date=new Date();

//创建一个日期对象

//原型链如下:

//date--->Date.prototype--->Object.pprototype--->null

使用构造函数的方法

在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。——MDN

demo如下:

function fun(){

   this.propA='prop';

   this.propB=3;

}

fun.prototype={

   methodA:function(){

       console.log(this.propA)

   }

}

var o=new fun();

o.methodA();//prop

// o是生成的对象,他的自身属性有'propA'和'propB'.

// 在o被实例化时,o.[[Prototype]]指向了fun.prototype.

使用Object.create创建对象

ECMAScript 5 中引入了一个新方法:Object.create()。可以调用这个方法来创建一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数:

var a = {a: 1};

// a ---> Object.prototype ---> null

var b = Object.create(a);

// b ---> a ---> Object.prototype ---> null

console.log(b.a); // 1 (继承而来)继承概念下面会讲

var c = Object.create(b);

// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);

// d ---> null

console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype

使用 class 关键字

ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不一样的。 JavaScript 仍然是基于原型的。这些新的关键字包括 class, constructor, static, extends, 和 super.

"use strict";

class Polygon {

 constructor(height, width) {

   this.height = height;

   this.width = width;

 }

}

//定义一个类Polygon

class Square extends Polygon {

 constructor(sideLength) {

   super(sideLength, sideLength);

 }//使用super引用父类

 get area() {

   return this.height * this.width;

 }

 set sideLength(newLength) {

   this.height = newLength;

   this.width = newLength;

 }

}

//使用extends定义Squeare继承父类Polygon

var square = new Square(2);//实例对象

//此时的原型链为:

//square--->Square.prototype--->Polygon.prototype--->Object.prototype--->null

//如果不理解为什么是这样,不要紧接着往下看类的说明

继承

其实在上面讲原型链的时候难以避免的也提到了继承,比如来自MDN的这个实例:

// 假定有一个对象 o, 其自身的属性(own properties)有 a 和 b:

// {a: 1, b: 2}

// o 的原型 o.[[Prototype]]有属性 b 和 c:(someObject.[[Prototype]] 符号是用于指派 someObject 的原型。这个等同于 JavaScript 的 __proto__  属性(现已弃用)。。从 ES6 开始, [[Prototype]] 可以用Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问。)

// {b: 3, c: 4}

// 最后, o.[[Prototype]].[[Prototype]] 是 null.

// 这就是原型链的末尾,即 null,

// 根据定义,null 没有[[Prototype]].

// 综上,整个原型链如下:

// {a:1, b:2} ---> {b:3, c:4} ---> null

console.log(o.a); // 1

// a是o的自身属性吗?是的,该属性的值为1

console.log(o.b); // 2

// b是o的自身属性吗?是的,该属性的值为2

// o.[[Prototype]]上还有一个'b'属性,但是它不会被访问到.这种情况称为"属性遮蔽 (property shadowing)".

console.log(o.c); // 4

// c是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有.

// c是o.[[Prototype]]的自身属性吗?是的,该属性的值为4

console.log(o.d); // undefined

// d是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有.

// d是o.[[Prototype]]的自身属性吗?不是,那看看o.[[Prototype]].[[Prototype]]上有没有.

// o.[[Prototype]].[[Prototype]]为null,停止搜索,

// 没有d属性,返回undefined

—-以上内容来自MDN继承与原型链

我想看到这里还是有些似懂非懂吧,那么来个例子吧:

var object1={

   a:1,

   b:function(){

       console.log(this.a+1);

   }

}

//定义一个对象

var object2=Object.create(object1);

//调用Object.create来创建一个新对象,新对象的原型就是调用 create 方法时传入的第一个参数,现在的原型链是:

//object2--->object1--->object1.prototype--->null

object2.d=4;

object2.a;

//1

//继承了object1的属性a

object2.b();

//2

//继承了object1的方法b

Object.getPrototypeOf(object2);

//object1得到object2的原型object1

我想现在应该是明白了吧,再不明白博主也是无能为力了,表达能力实在有限。

2. 类

总算说到类了,由于javascript的类基于原型链和继承,因此在上面的内容中就已经定义了很多的类。咱们javascript的类同样能实现传统类的多态,封装,继承等特性,这里主要讲解了继承这个概念,但实际上很多时候不经意可能就用了这三个特性。很好玩不是么

首先,我们先看下在ES5中定义一个类的形式:

function Animal(name) {

   this.name = name;

   this.sleep = function() {

       console.log(this.name+'正在睡觉');

   }

}//定义Animal类

Animal.prototype = {

   eat: function(food) {

       console.log(this.name+"正在吃"+food);

   }

}

function Cat() {

}

Cat.prototype = new Animal('Tom');

var Tom = new Cat('Tom');/Cat实例对象

Tom.eat('猫粮');

//Tom正在吃猫粮

//继承Animal方法

Tom.sleep();

//Tom正在睡觉

//继承Animal方法

//现在的原型链:

//Tom(Cat实例对象)--->Cat.prototype(Animal实例对象)--->Animal.prototype--->Object.prototype--->null

好的,然后我们看下在ES6中改写上面的类:

class Animal {

 constructor(name) {

   this.name = name;

 }

 sleep() {

   console.log(this.name + ' 正在睡觉');

 }

 eat(food){

   console.log(this.name+'正在吃'+food)

}

}

class Cat extends Animal {

}

const Tom = new Cat('Tom');

Tom.eat('猫粮');

//Tom正在吃猫粮

//继承Animal方法

Tom.sleep();

//Tom正在睡觉

//继承Animal方法

//现在的原型链:

//Tom(Cat实例对象)--->Cat.prototype(Animal实例对象)--->Animal.prototype--->Object.prototype--->null

定义一个类的方法实际上也是上面所说的定义一个对象的方法,类本身就是一个对象,只不过这个对象里面的方法和属性可以供许多实例对象调用而已。  

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

推荐阅读更多精彩内容