四、构造函数和原型对象

1. 构造函数

构造函数就是用new创建对象时调用的函数。使用构造函数的好处在于所有用同一个构造函数创建的对象都具有同样的属性和方法。

function Person(){}
var p1 = new Person();
console.log(p1.constructor === Person); // true
console.log(p1 instanceof Person);      // true

可以使用构造函数来检查一个对象的类型,但还是建议使用instanceof来检查对象类型。因为构造函数属性可以被覆盖,并不一定完全准确。
示例:构造函数返回对象

function Foo(){this.name = "foo";return {name: "hhh"};
}
var f = new Foo();
f.name;         // hhh
f.constructor;  // Object

示例:构造函数返回原始类型

function Too(){this.name = "too";return "hhh";
}
var t = new Too();
t.name;         // too
t.constructor;  // Too

构造函数中显示调用return:

  • 如果返回的值是一个对象,它会替代新创建的对象实例返回;
  • 如果返回的值是一个原始类型,它会被忽略,新创建的对象实例会被返回。

2. 原型对象

请参照:【详解prototype与proto区别 】

3. 改变原型对象

[[Prototype]]属性只是包含一个指向原型对象的指针,并不是一个副本;任何对原型对象的改变都立刻反映到所有引用它的对象实例上。
示例:扩展原型对象

function Person(){}
var p = new Person();
p.sayHi();  //  p.sayHi is not a function(…)
Person.prototype.sayHi = function(){console.log("hi");
};
p.sayHi();  // "hi"

注意:在一个对象上使用Object.seal()Object.freeze()时,完全是在操作对象的自有属性,可以通过在原型上添加属性来扩展这些对象实例。
示例:冻结对象

function Person(name){}
var p = new Person();
Object.freeze(p);
p.name = "ligang";
Person.prototype.sayHi = function(){console.log("hi");
};
console.log(p.name);    // undefined
p.sayHi();  // "hi"

关于对象,请查看:【面向对象的程序设计】、【JavaScript高级技巧-防篡改对象】

五、继承

JavaScript内建的继承方法被称为原型对象链,又称为原型对象继承。当一个对象的[[Prototype]]设置为另一个对象时,就在这两个对象之间创建了一条原型对象链。

1. Object.prototype

所有对象都继承自Object.prototype

方法 说明
hasOwnProperty() 检查是否存在一个给定名字的自有属性
propertyIsEnumerable() 检查一个自有属性是否可枚举
isPrototypeOf() 检查一个对象是否是另一个对象的原型对象
valueOf() 返回一个对象的值表达
toString() 返回一个对象的字符串表达

上述5种方法经由继承出现在所有对象中。
(1)valueOf()
valueOf()默认返回对象实例本身,可以定义自己的valueOf()方法,定义的时候没有改变操作符的行为,仅仅定义了操作符默认行为所使用的值。
(2)toString()
一旦valueOf()返回的是一个引用而不是原始值的时候,就会回退调用toString()
示例:

var obj1 = {valueOf: function(){return "valueOf";},toString: function(){return "toString";}
}
var obj2 = {valueOf: function(){return {name: "哈哈"};},toString: function(){return "toString";}
}
obj1 + "";  // "valueOf"
obj2 + "";  // "toString"

2. 修改Object.prototype

Object.prototype添加方法,默认是可枚举的,意味着可以出现在for-in循环中。Douglas Crockford(JavaScript之父)推荐在for-in循环中始终使用hasOwnProperty()

var empty = {};
Object.prototype.myName = "LIGANG";
for(var prop in empty){console.log(prop);      // 会输出:myName
}
for(var prop in empty){if(empty.hasOwnProperty(prop)){console.log(prop);  // 无任何内容输出}
}

所以,在进行for-in操作时,最好的方式就是增加hasOwnProperty()判断;与此时同,不要修改Object.prototype

3. 对象继承

对象继承是最简单的继承类型,需要做的就是指定哪个对象是新对象的[[Prototype]]
创建对象过程中,字面量形式会隐式指定Object.prototype为其[[Prototype]],也可以用Object.create()方式显示指定。

var obj1 = {};
var obj2 = Object.create(Object.prototype, {});

Object.create()方法,第一个参数是需要设置为新对象的[[Prototype]]的对象,第二个参数是一个属性描述对象,格式同Object.defineProperties()
示例:原型对象链(继承)

var person = {name: "person",sayName: function(){console.log(this.name);}
};
var p1 = Object.create(person, {name: {cofigurable: true,enumerable: true,value: "ligang",writable: true}
});     


末端通常是一个Object.prototype[[prototype]]被置为null。
示例:空对象

var obj1 = {};
var obj2 = Object.create(null);
"toString" in obj1; // true
"toString" in obj2; // false

obj2没有原型对象链的对象。完全是一个没有任何预定义属性的白板,使其成为一个完美的哈希容器。

4. 构造函数继承

构造函数的所有对象实例共享同一个原型对象,所以它们都继承自该对象,但不能用原型对象继承自有属性。

function Person(name){this.name = name; // 私有属性
}
Person.prototype.sayName = function(){  // 共用方法console.log(this.name);
};
var p1 = new Person("ligang");
var p2 = new Person("camile");

p1,p2都是构造函数Person的实例,其共享Person.prototype原型对象。但其自有属性name不能通过原型对象继承。
总结:自有属性/方法通过构造函数定义,共有属性/方法通过原型对象继承!!!

六、对象模式

虽然JavaScript没有一个正式的私有(局部)属性的概念(ES6中出现了let语法,可以定义局部变量),但是可以创建仅在对象内可以访问的数据或函数。使用模块模式可对外界隐藏数据;也可以使用立即调用函数表达(IIFE)定义仅可被新创建的对象访问的本地变量和函数。

1. 私有成员和特权成员

var obj = (function(){// 私有变量var author = "ligang";return {// 公共的方法和属性getAuthor: function(){return author;}};
}());
obj.author; // undefined
obj.getAuhtor(); // "ligang"

上述函数仅存在于被调用的瞬间,一旦执行后立即就被销毁了。

// 上述示例的等价写法
var obj = (function(){// 私有变量var author = "ligang";var getAuthor = function(){return author;};return {// 公共的方法和属性getAuthor: getAuthor};
}());

2. 混入

混入将一个属性从一个对象复制到另一个,从而使得接受者在不需要继承提供者的情况下获得其功能。和继承不同,混入令你在创建对象后无法检查属性来源。若你想要获得更强大的功能且需要知道该功能来自哪里,继承是首选!

function mixin(des, src){for(var prop in src){if(src.hasOwnProperty(prop)){des[prop] = src[prop];}}return des;
}

注意上述方式,不是深拷贝!
奇舞团提供了深拷贝方式:https://github.com/75team/mixin.js

var a = {x:{y:1, z:3}};
mixin(a, {x:{y:2}, z:2}, function(a,b){try{return mixin(a,b, arguments.callee)}catch(ex){return b}
});

等价于 jQuery.extend(true, a, {x:{y:2}, z:2});

3. 作用域安全的构造函数

function Person(name){this.name = name;
}
var p1 = new Person("ligang");
var p2 = Person("camile");
console.log(p1 instanceof Person); // true
console.log(p2 instanceof Person); // false

作用域安全的构造函数是用不用new都可以被调用来生成新的对象实例的构造函数。其this在构造函数一开始执行就已经指向自定义类型的实例。当然,你可以根据new的使用与否决定构造函数的行为。

function Person(name){if(this instanceof Person){this.name = name;}else {return new Person(name);}}
var p = Person("ligang");
console.log(p instanceof Person); // true

许多内建构造函数,例如Array、RegExp不需要new也可以工作,正是因为它们被设计之初采用了作用域安全的构造函数。

JavaScript面向对象精要(二)相关推荐

  1. 《JavaScript面向对象精要》读书笔记

    JavaScript(ES5)的面向对象精要 标签: JavaScript 面向对象 读书笔记 2016年1月16日-17日两天看完了<JavaScript面向对象精要>(参加异步社区的活 ...

  2. 《JavaScript面向对象精要》——1.8 原始封装类型

    本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.8节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...

  3. 《JavaScript面向对象精要》——第1章 原始类型和引用类型1.1 什么是类型

    本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.1节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...

  4. 《JavaScript面向对象精要》——1.9 总结

    本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.9节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...

  5. 《JavaScript面向对象精要》——第1章 原始类型和引用类型 1.1 什么是类型

    本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.1节,作者:[美]Nicholas C. Zakas著,更多章节内容可以访问云栖社区"异步社区&qu ...

  6. 《JavaScript面向对象精要》——1.2 原始类型

    本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.2节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...

  7. 《JavaScript 面向对象精要》 读书笔记

    <JavaScript 面向对象精要> 读书笔记 高程面向对象这块内容介绍的比较浅显,个人觉得这本小书是高程的补充,看完之后觉得收获匪浅,所以做了个笔记,以备后询 1. 原始类型和引用类型 ...

  8. JavaScript 面向对象编程(二) —— 构造函数 / 原型 / 继承 / ES5 新增方法

    本篇为 JavaScript 进阶 ES6 系列笔记第二篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 : Javascript 继承机制的 ...

  9. javascript面向对象技术基础(二)

    数组 我们已经提到过,对象是无序数据的集合,而数组则是有序数据的集合,数组中的数据(元素)通过索引(从0开始)来访问,数组中的数据可以是任何的数据类型.数组本身仍旧是对象,但是由于数组的很多特性,通常 ...

最新文章

  1. RabbitMQ 简介以及使用场景
  2. python3+django写的个人笔记博客
  3. 运行vc++6.0出现Error spawning cl.exe问题解决方法
  4. Java内存模型与指令重排
  5. 洛谷 P3521 [POI2011]ROT-Tree Rotations 解题报告
  6. java中线程调度遵循的原则_深入理解Java多线程核心知识:跳槽面试必备
  7. gradle 查看依赖类库版本_Android studio中查看依赖的第三方库的历史版本和最新版本...
  8. 小米CC9 白色恋人版实拍图赏:这颜值 很小米!
  9. java 企业号 临时素材_查看“获取临时素材文件”的源代码
  10. 关于C++的建议,仅仅为了规范代码(一)
  11. nginx 504 Gateway Time-out 解决办法
  12. 不同编程语言下CH347DLL的调用方法
  13. Python--正则表达式处理文本
  14. C++function,future,packaged_task
  15. git仓库服务器SSH认证示例
  16. send_nsca passive monitor setup notes
  17. Android应该怎么学
  18. 关键路径法详解【CPM】
  19. [游戏数据分析]WAU模型简介及WAU预测
  20. JAVA 软加密之加密和校验过程图

热门文章

  1. MySQL触发器基本实操
  2. java查看制表符_java-制表符\t的使用说明
  3. 2019年5月23日 AY 程序员调侃语录
  4. AutoCAD Electrical 2022—项目特性
  5. 初次戴隐形眼镜 须知。
  6. 在线VIM编辑器模拟工具
  7. 公司海外邮箱注册指南,外贸公司邮箱注册流程
  8. /layers_common.avx512_skx.cpp.obj] Error 1 invalid register for .seh_savexmm
  9. [usaco月赛]梦幻王国
  10. HoloLens2投屏