概述

放假读完了《你不知道的javascript》上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用。

这篇笔记是这本书的下半部分,上半部分请见《你不知道的javascript》读书笔记1

误区

经常可以在js中听到一句话,万物皆对象,其实在某种意义上来说,这句话是错的。因为js中还有很多对象的子类型,比如函数,数组,内置对象等,他们除了有对象的性质之外,还具有一些特别的行为,严格说来,他们不等于对象。

在其它语言中,属于对象的函数通常被称为方法。因此我们经常把对象里面的函数称为方法。但是严格说来,对象里面的函数并不属于这个对象,对象中的这个函数在很多情况下都只是个引用而已。

怎么复制对象

复制对象有深复制和浅复制。浅复制非常简单,es6定义了如下方法来复制一个对象。

  1. var newObj = Object.assign({}, myObject);

另外,对于JSON安全的对象来说,有下面这种巧妙的复制方法。JSON安全指的是被序列化为一个JSON字符串后再解析回来的对象和原对象完全一样。

  1. var newObj = JSON.parse(JSON.stringify(someObj));

对象遍历

  1. 可以用for..in来遍历对象的可枚举属性列表。
  2. 可以用map(),some(),every()来遍历数组。
  3. es6中定义了for..of来遍历数组的值。

在js中实现类

我们引入类这种设计模式,就是为了简化代码的书写。类这种设计模式通过下面三种方式简化代码书写:

  1. 混入。把一个类的属性和方法直接扔到另一个类里面去,来复制前一个类的代码。
  2. 继承。使一个子类继承它的父类,让子类复制父类的所有代码。
  3. 实例。通过创建一个实例,让实例复制类的方法。

由于js中最基本的是对象,所以首先来看怎么用对象来"实现"一个类,即给对象实现上面的三种代码复制方式。

在对象之间实现混入其实很简单,就是对象的复制。代码如下:

  1. // 非常简单的 mixin(..) 例子 :
  2. function mixin( sourceObj, targetObj ) {
  3. for(key in sourceObj) {
  4. if(!(key in targetObj)) {
  5. targetObj[key] = sourceObj;
  6. }
  7. }
  8. }
  9. var Vehicle = {
  10. engines: 1,
  11. ignition: function() {
  12. console.log( "Turning on my engine." );
  13. },
  14. drive: function() {
  15. this.ignition();
  16. console.log( "Steering and moving forward!" );
  17. }
  18. };
  19. var Car = mixin( Vehicle, {
  20. wheels: 4,
  21. drive: function() {
  22. Vehicle.drive.call( this );
  23. console.log(
  24. "Rolling on all " + this.wheels + " wheels!"
  25. );
  26. }
  27. } );

接下来是继承,继承其实还好,和混入差不多,但是第三种实例呢?怎么通过对象来生成一个实例?由于对象本来就是js中最小的单位,那还怎么生成更小的单位?

所以我们考虑复杂一点的对象,比如函数,然后函数实例化后就可以得到一个对象了。

那么对于函数,怎么实现第一种方法混入呢?很简单,用call()函数,代码如下:

  1. function Vehicle() {
  2. this.engines = 1;
  3. //实例方法
  4. this.ignition = function() {
  5. console.log( "Turning on my engine." );
  6. }
  7. }
  8. //原型方法
  9. Vehicle.prototype.drive = function() {
  10. this.ignition();
  11. console.log( "Steering and moving forward!" );
  12. };
  13. function Car() {
  14. //混入Vehicle的属性和实例方法
  15. Vehicle.call(this);
  16. //自己的属性
  17. this.engines = 2;
  18. //混入Vehicle的原型方法
  19. for(key in Vehicle.prototype) {
  20. Car.prototype[key]=Vehicle.prototype[key];
  21. }
  22. //然后可以定义自己的方法
  23. }

然后是第二种方法继承,继承和混入差不多,可以用上面的来实现,也可以用实例来实现,书上有很多方法,我就不写了。

最后是第三种方法实例。用new关键字可以给函数构造一个对象,我们可以叫它为实例,因为这个对象复制了构造函数的代码。代码如下:

  1. var car = new Car();

这样就完成了!我们用js中的函数对象实现了一个类。看起来很美好,但是用起来不是那么的舒服。原因是js中对于对象的复制并不是复制代码,而是复制引用!

也就是说,在用函数对象实现一个类的过程中,混入和继承实际上并没有复制代码,而是在复制引用!这就是js糟糕的继承方式,也是实现类最大的痛点。

值得注意的是,由于第三种方法实例是在复制实例属性和方法的代码(虽然也是在引用原型方法的代码),所以有些人用第三种方法实例来实现第二种方法继承,这就是各种继承方法的本质由来。而这些带有技巧性并且难读的复杂方法,都或多或少的带有另一些痛点。

最后es6推出了class关键字来定义一个类,并规范化了混入,继承和实例,对很多方法进行了一些官方的封装。不得不说这个改变使得类在js中方便书写了很多。

原型

函数有一条特殊的性质,那就是可以通过new来获得一个对象。为了实现共享的方法,在获得这个对象的同时,会有一个不可枚举的属性[[Prototype]]被附加到了这个对象中,这个属性就是我们所说的原型。这个属性被关联到了构造函数的prototype属性。这样,所有通过new的子对象都可以访问构造函数的prototype中的同一个方法。值得注意的是,这里并不是复制,而是引用。

  1. function Foo() {
  2. // ...
  3. }
  4. var a = new Foo();
  5. Object.getPrototypeOf( a ) === Foo.prototype; // true

而构造函数的prototype属性是怎么来的呢?答案是,这个属性是所有的函数自带的属性。

值得注意的是,对象的原型的作用不仅仅是为了访问构造函数的共享方法,它也是对象之间互相访问的桥梁。

那么这个[[Prototype]]到底是什么?它其实有一个名字,叫做proto。它的实现大致是这样的:

  1. Object.defineProperty( Object.prototype, "__proto__", {
  2. get: function() {
  3. return Object.getPrototypeOf( this );
  4. },
  5. set: function(o) {
  6. // ES6 中的 setPrototypeOf(..)
  7. Object.setPrototypeOf( this, o );
  8. return o;
  9. }
  10. } );

一般来说,我们并不直接调用__proto__来访问对象的原型,那我们怎么对对象的原型进行各种关联操作呢?代码如下:

  1. var foo = {
  2. something: function() {
  3. console.log( "Tell me something good..." );
  4. }
  5. };
  6. var bar = Object.create( foo );
  7. bar.something(); // Tell me something good...

Object.create的简化的源码如下。由于Object.create是在es5中声明的,所以下面的代码也可以当做polyfill代码。

  1. if (!Object.create) {
  2. Object.create = function(o) {
  3. function F(){}
  4. F.prototype = o;
  5. return new F();
  6. };
  7. }

委托

委托是另一种设计模式,它非常适合js,示例代码如下:

  1. Task = {
  2. setID: function(ID) { this.id = ID; },
  3. outputID: function() { console.log( this.id ); }
  4. };
  5. // 让 XYZ 委托 Task
  6. XYZ = Object.create( Task );
  7. XYZ.prepareTask = function(ID,Label) {
  8. this.setID( ID );
  9. this.label = Label;
  10. };
  11. XYZ.outputTaskDetails = function() {
  12. this.outputID();
  13. console.log( this.label );
  14. };
  15. // ABC = Object.create( Task );
  16. // ABC ... = ...

这段代码的易读性很好,相比较类这种设计方式来说,可以直接看出我们要干什么。

个人觉得对于小型系统,用委托这种设计模式远远优于类设计模式。对于复杂的系统,我还没有体验过,不好妄下判断。

《你不知道的javascript》读书笔记2的更多相关文章

  1. <数据挖掘导论>读书笔记11异常检测

    异常检测的目标是发现与大部分其他对象不同的对象.通常,异常对象被称作离群点(Outlier). 异常检测也称偏差检测(Deviation detection),因为异常对象的属性值明显偏离期望的或者常 ...

  2. <数据挖掘导论>读书笔记10聚类分析续

    基于原型的聚类 模糊c均值使用模糊逻辑和模糊集合论的概念,提出一种聚类方案,它很像K均值,但是不需要硬性地将对象分派到一个簇中.模糊c均值算法有时也称为FCM 混合模型聚类采取这样的访谈,簇集合可以用 ...

  3. <数据挖掘导论>读书笔记9聚类分析

    1. 聚类分析仅根据在数据中发现的描述对象及其关系的信息,将数据对象分组. 其目标是组内的对象相互之间是相似的或者相关的,而不同组中的对象是不同的或者不相关的. 2.聚类分析的重要技术 K均值:K均值 ...

  4. <数据挖掘导论>读书笔记8FP树

    1FP树

  5. <数据挖掘导论>读书笔记7 Apriori算法

    Apriori算法是一种最有影响的挖掘布尔关联规则频繁项集的算法.其核心是基于两阶段频集思想的递推算法.该关联规则在分类上属于单维.单层.布尔关联规则.在这里,所有支持度大于最小支持度的项集称为频繁项 ...

  6. <数据挖掘导论>读书笔记4--其他分类技术

    1.基于规则的分类器 2.最近邻分类器 3.贝叶斯分类器 4.人工神经网络 5.支持向量机 6.组合方法 7.不平衡类问题 8.多类问题

  7. <数据挖掘导论>读书笔记6关联分析的高级概念

    处理联系属性: 基于离散化的方法 基于统计学的方法 非离散化方法 处理概念分层 定义在一个特定领域的各种实体或者概念的多层组织.概念分层可以用有向无环图DAG来标示. 序列模式 可选计数方案 COBJ ...

  8. <数据挖掘导论>读书笔记5关联分析的基本概念和算法

    关联规则的强度可以用support度和confidence(置信)度来度量 关联规则发现  给定事务的集合T,关联规则发现是指找出支持度大于等于minsup并且置信度大于等于minconf的所有规则, ...

  9. <数据挖掘导论>读书笔记3--分类

    1.分类的基本概念 分类任务就是通过学习得到一个目标函数f,把每个属性集x映射到一个预先定义的类标号y 目标函数也称为分类模型. 2. 解决分类问题的一般方法: 决策树分类法 基于规则的分类法 神经网 ...

  10. <数据挖掘导论>读书笔记2

    1.频率和众数 frequency(vi)=具有属性值vi的对象数/m 分类属性的众数mode是具有最高频率的值. 2.百分位数 3.位置度量:均值和中位数 4.散布度量:极差和方差 绝对平均偏差 A ...

随机推荐

  1. Maven项目中遇到的问题及其解决方案

    Maven中pom报红 1.jdk版本是否符合要求? 2.maven的本地confg中的setting.xml中是否和要求的jdk版本一致? 3.maven本地仓库路径是否正确,即为自己的确定的仓库位 ...

  2. Java中锁的实现与内存语义

    目录 1. 概述 2. 锁的内存语义 3. 锁内存语义的实现 4. 总结 1. 概述 锁在实际使用时只是明白锁限制了并发访问, 但是锁是如何实现并发访问的, 同学们可能不太清楚, 下面这篇文章就来揭开 ...

  3. 本学期c#学习总结

    本学期c#学习总结 时间转瞬即逝,大一上半学期的学习生涯已经结束.虽然以前我没什么关于学习计算机的基础,但是经过了这几个月的学习我也还是有点收获的. 我发现c#语言的关键词有很多语言特性和固定的用法, ...

  4. springboot 整合 mybatis

    spirngboot 整合mybatis有两种方式 第一种 无配置文件注解版,这一种符合springboot的风格 第二种 有配置文件xml版本,这一种就是传统的模式 无论哪一种,首先都需要加入MyS ...

  5. PowerScript语言基础

    注释: 以 "//" 开头,其后书写注释内容,常用于单行注释. "/-/"中间的部分为注释,便于多行说明. //这是一个单行注释 INTEGER I I = I ...

  6. Swift 反射机制,命名空间

    1. 知道 Swift 中有命名空间        - 在同一命名空间下,全局共享!        - 第三方框架使用 Swift 如果直接拖拽到项目中,从属同一个命名空间,很有可能冲突!       ...

  7. service 设计问题

    今天写了一段让自己尴尬的代码,就是在一个方法中调用了两个 service 方法,而我为每个service 都定义了 事物回滚. 然后郁闷了,我执行请求调用该方法, 发现第二个service方法执行失败 ...

  8. 【APP测试(Android)】--安装卸载

  9. HBase总结(十一)hbase Java API 介绍及使用示例

    几个相关类与HBase数据模型之间的对应关系 java类 HBase数据模型 HBaseAdmin 数据库(DataBase) HBaseConfiguration HTable 表(Table) H ...

  10. 充分利用CPU多核的处理能力 innodb_read_io_threads和innodb_write_io_threads

    https://book.2cto.com/201402/40300.html 在MySQL5.1.X版本中,innodb_file_io_threads参数默认是4,该参数在Linux系统上是不可更 ...