JavaScript面向对象精要(二)
四、构造函数和原型对象
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面向对象精要(二)相关推荐
- 《JavaScript面向对象精要》读书笔记
JavaScript(ES5)的面向对象精要 标签: JavaScript 面向对象 读书笔记 2016年1月16日-17日两天看完了<JavaScript面向对象精要>(参加异步社区的活 ...
- 《JavaScript面向对象精要》——1.8 原始封装类型
本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.8节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...
- 《JavaScript面向对象精要》——第1章 原始类型和引用类型1.1 什么是类型
本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.1节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...
- 《JavaScript面向对象精要》——1.9 总结
本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.9节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...
- 《JavaScript面向对象精要》——第1章 原始类型和引用类型 1.1 什么是类型
本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.1节,作者:[美]Nicholas C. Zakas著,更多章节内容可以访问云栖社区"异步社区&qu ...
- 《JavaScript面向对象精要》——1.2 原始类型
本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.2节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...
- 《JavaScript 面向对象精要》 读书笔记
<JavaScript 面向对象精要> 读书笔记 高程面向对象这块内容介绍的比较浅显,个人觉得这本小书是高程的补充,看完之后觉得收获匪浅,所以做了个笔记,以备后询 1. 原始类型和引用类型 ...
- JavaScript 面向对象编程(二) —— 构造函数 / 原型 / 继承 / ES5 新增方法
本篇为 JavaScript 进阶 ES6 系列笔记第二篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 : Javascript 继承机制的 ...
- javascript面向对象技术基础(二)
数组 我们已经提到过,对象是无序数据的集合,而数组则是有序数据的集合,数组中的数据(元素)通过索引(从0开始)来访问,数组中的数据可以是任何的数据类型.数组本身仍旧是对象,但是由于数组的很多特性,通常 ...
最新文章
- python os 文件操作 os.rename(src, dst) 方法 os.renames()
- 英特尔 620 显卡 驱动 七代cpu_英特尔的智能“整体厨房”
- python qt 按钮_PyQt(Python+Qt)学习随笔:toolButton的popupMode属性
- win10 edge默认浏览器设置更改教程
- (转)postgresql+postgis空间数据库使用总结
- 服务器进bios修改启动顺序,服务器进入bios设置u盘启动
- 达摩院 2020 预测:感知智能的“天花板”和认知智能的“野望”
- 在哪里学python-为什么要选择学python,亮点在哪呢?
- 深度 | 人工智能究竟能否实现?
- 数据结构——二叉链表
- CSS文本框里的字_把网站搬进PPT里是种怎样的体验?
- 大数据Hadoop入门
- Java数组怎么传给形参_在Java程序中,如果方法的形参是数组,则调用该方法时传递的是数组的...
- 【经验分享】用PS如何将图片的四角做成圆弧角
- 教你怎么一下哄好赌气的女朋友​
- 实时风控引擎项目部署
- 如何准备pmp考试?
- 【node.js】识别图片中的文字
- Win10在线升级Win11(绕过TPM2.0)
- 「DaoCloud道客」全新 IT 交付:服务化 + 标准化 = 一致的自助式服务体验
热门文章
- 关于 非分页缓冲池 内存占用过高但任务管理器无程序 的特殊原因
- 表示自己从头开始的句子_关于从头开始的好句子
- my sql常用的数据类型
- linux系统微擎模块,微擎模块:轻松筹9.1开源版源码基于微信社交圈的筹款模块
- Win7系统下载速度很快,上传速度超慢的解决
- c语言图片透明度混合,【PS CC 2018 学习连载 19】如何让图层与图层之间融合的更好?不透明度和混合模式详细讲解...
- 英国留学生本科生毕业论文应该多长?
- 计算机能力提升选网络研修,信息技术应用能力提升网络研修心得范文
- PyCharm 导包提示 unresolved reference完整解决方案
- Airbnb 规范大全