(转载)JavaScript中的原型和对象机制
(转载)http://www.cnblogs.com/FlyingCat/archive/2009/09/21/1570656.html
1 对象相关的一些语言特性
1.1 一切皆为对象
JavaScript里所有的东西都是对象. 对象是属性的集合. 数字, 字符串, 布尔值等原始值是"伪对象", 它们同样拥有属性, 但是是在栈上分配并按值传递. 而其他的对象是堆上分配并按引用传递.
一个很重要的概念是, 函数也是对象, 能够作为变量的值, 返回值, 参数或者属性的值. 函数对象特殊的地方是能通过"xxx()"语法执行包含在xxx函数对象内的代码. 因为这一特殊性, typeof xxx 将会返回function, 但这只是一种便利设施.
1.2 对象的属性可以动态添加和删除
var foo = new Object();
// 为foo对象添加bar属性
foo.bar = "foobar";
alert(foo.bar); //foobar
// 删除foo对象的bar属性
delete foo.bar;
alert(foo.bar); //undefined
1.3 除了宿主对象, 其它对象皆由构造函数创建
要有对象, 就先要有创建对象的方法.
在C++/Java等语言, 这个方法就是实例化XXX类的一个实例xxx.
而在JavaScript的世界里实际没有类的东西, 当然仍然可以用"类"和"实例"等惯用语来描述JavaScript中类似的行为, 但其机制是完全不同的. JavaScript的对象是由构造函数创建的, 每个对象都有constructor属性表示创建该对象的构造函数:
function Test() { this.a = "hello"; }
var test = new Test(); // 由Test构造函数创建
alert(test.constructor); var o = { a: "hello" };
//实际相当于
var o_ = new Object();
o_.a = "hello"; // 由Object构造函数创建
alert(o.constructor);
构造函数也是对象, 那构造函数是由什么创建? 内建的Function函数:
function Test(a, b)
{
alert(a+b);
}
// 相当于:
Test = new Function(["a", "b"], "alert(a+b);");
Function函数又是由什么创建? 实际上Function是本机代码实现的固有对象. 不过为了一致性, Function也有constructor属性, 该属性指向它自己. 接上面的代码:
/* 输出 function Function(){
[native code]
}
*/
alert(Test.constructor); alert(Test.constructor.constructor === Test.constructor); // true
alert(Test.constructor === Object.constructor); // true
2 原型prototype
2.1 prototype的概念
prototype是构造函数的一个属性, 该属性指向一个对象. 而这个对象将作为该构造函数所创建的所有实例的基引用(base reference), 可以把对象的基引用想像成一个自动创建的隐藏属性. 当访问对象的一个属性时, 首先查找对象本身, 找到则返回; 若不, 则查找基引用指向的对象的属性(如果还找不到实际上还会沿着原型链向上查找, 直至到根). 只要没有被覆盖的话, 对象原型的属性就能在所有的实例中找到.
原型默认为Object的新实例, 由于仍是对象, 故可以给该对象添加新的属性:
// prototype默认为new Object(); 为了方便, 记为p_obj
function Person(name) {
this.name = name;
} // 为 p_obj 增加 sayName 属性
Person.prototype.sayName = function(){
alert(this.name);
} var john = new Person("John"); // john 的 base reference指向p_obj
var eric = new Person("Eric"); // eric 的 base reference也是指向p_obj // 注意sayName代码中的this将指向实例化后的对象(this绑定)
john.sayName(); // john对象本身没有sayName属性, 于是访问原型对象p_obj的sayName属性
eric.sayName(); // 访问同一个原型对象p_obj的sayName属性 var tmp = Person.prototype;
tmp.boss = "David";
// 于这个运行点, p_obj已经被修改
// 根据上述属性访问流程, 新的修改(boss属性)能反映到所有的实例, 包括已经创建和即将创建的
alert("John's boss is " + john.boss);
alert("Eric's boss is " + eric.boss); // hisCar和sayCar属性将增加到john对象而不是p_obj对象..
john.hisCar = "Audi";
john.sayCar = function(){
alert(this.name + " has a car of " + this.hisCar);
}
john.sayCar();
// ..因此下一句将错误, 因为eric对象本身和原型p_obj都没有sayName属性
/* eric.sayCar(); */
2.2 原型链
除了能修改prototype指向的对象, 还能修改prototype指向哪一个对象, 即为prototype赋予一个不同的对象. 这可以实现一种简单的继承:
function Superman() {}
Superman.prototype.sayHello = function(){
alert("I'm a superman.");
} function SupermanCan(skill){
this.skill = skill;
}
// 为prototype赋予Superman的实例..
SupermanCan.prototype = new Superman();
// ..再动态添加新的属性
SupermanCan.prototype.sayMore = function(){
this.sayHello(); // 调用"父类"的方法
alert("I can " + this.skill);
} var david = new SupermanCan("fly");
// output: I'm a superman. I can fly
david.sayMore();
如果先实例化出一个对象, 再为构造函数prototype赋予一个不同的对象, 将会: 已经创建的对象的基引用不变, 将来创建的对象的基引用为新的原型对象:
var f1 = {echo: function() { alert("sound"); } };
function Foo() {};
var foo = new Foo(); // foo的基引用指向Object实例
Foo.prototype = f1;
/* 未定义, 因为这是"foo对象自己或者基引用指向的对象有echo属性吗?"
而不是"foo对象自己或者Foo.prototype指向的对象有echo属性吗?" */
alert(foo.echo); var foo2 = new Foo(); // foo2的基引用指f1对象
foo2.echo(); // output: sound
所有的构造函数的prototype都不能为空, 就是说Superman.prototype = null 会被解释引擎无视; 另一方面, Object构造函数也有prototype属性(该属性是只读的, 可以为原型增加属性,但不能赋予不同的对象), 故因此可以有多层的原型链, 但原型链的根必定会是Object.prototype . 这意味着给Object.prototype增加属性影响到所有对象:
Object.prototype.echo = function() {
alert("hello");
} // echo属性将增加到所有对象固有对象和自定义对象 var arr = new Array();
arr.echo();
Array.echo(); function ObjCons() {
this.dummy = "d";
}
var obj = new ObjCons();
obj.echo();
ObjCons.echo();
3. 构造函数和new的实质
构造函数是一个地地道道的函数, 一个函数之所以能成为构造函数, 是因为new运算符:
this.msg = "window"; function Test()
{
alert(this.msg);
} Test(); // window
var test = new Test(); // undefined. 因为test对象没有定义msg属性
二者区别在于如何切入对象: Test() 在某个对象(例子中为window)的上下文上执行代码, 即this指向这个对象; new Test()创建一个新对象, 并以这个新的对象为上下文(this指向新对象)执行代码, 然后返回这个新对象.
假如有个函数:
function Test() {
var dummy = "have money";
this.wish = dummy;
doSomeThing();
}
结合以上的所有论述, 可以推测new Test()行为的伪代码表示为:
创建一个新对象temp;
temp.constructor = Test;
temp.(base reference) = Test.prototype; // 这一句先于代码体执行, 意味着构造函数里的this.xxx能访问原型对象的属性xxx
bind: this = temp; // 将this绑定到temp对象
// 开始执行函数代码
var dummy = "have money";
this.wish = dummy; // 为temp对象添加wish属性
doSomeThing();
....
// 结束执行函数代码
return temp;
这个未必会符合内部的二进制实现, 但却能很好地解释了JavaScript的特性.
(转载)JavaScript中的原型和对象机制的更多相关文章
- JavaScript中的原型和对象机制
1.对象相关的一些语言特性 JavaScript里所有的东西都是对象, 对象是属性的集合.要知道,函数也是对象, 能够作为变量的值. 返回值. 参数或者属性的值. 函数对象特殊的地方是能通过" ...
- 深入了解JavaScript中基于原型(prototype)的继承机制
原型 前言 继承是面向对象编程中相当重要的一个概念,它对帮助代码复用起到了很大的作用. 正文 Brendan Eich在创建JavaScript时,没有选择当时最流行的类继承机制,而是借鉴Self,用 ...
- (转载)JavaScript中的Window窗口对象
(转载)http://www.ijavascript.cn/jiaocheng/javascript-window-65.html 例子: <html> <head> < ...
- JavaScript中的 原型 property 构造函数 和实例对象之间的关系
1 为什么要使用原型? /* * javascript当中 原型 prototype 对象 * * */ //首先引入 prototype的意义,为什么要使用这个对象 //先来写一个构造函数的面向对象 ...
- 【转】JavaScript中的原型和继承
请在此暂时忘记之前学到的面向对象的一切知识.这里只需要考虑赛车的情况.是的,就是赛车. 最近我正在观看 24 Hours of Le Mans ,这是法国流行的一项赛事.最快的车被称为 Le Mans ...
- javascript 中的原型继承
javascript圆形变成的基本规则: 所有数据都是对象: 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它: 对象会记住它的原型: 如果对象无法响应某个请求,它会把这个请求委托给 ...
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- javascript中的原型继承
在Javascript面向对象编程中,原型继承不仅是一个重点也是一个不容易掌握的点.在本文中,我们将对Javascript中的原型继承进行一些探索. 基本形式 我们先来看下面一段代码: <cod ...
- 图解JavaScript中的原型链
转自:http://www.jianshu.com/p/a81692ad5b5d typeof obj 和 obj instanceof Type 在JavaScript中,我们经常用typeof o ...
随机推荐
- iOS 制作发布证书,发布到App Store
---恢复内容开始--- 1.登陆 iOS Dev Center 选择进入iOS Provisioning Portal. 2.在 iOS Provisioning Portal中,点击App IDs ...
- objective-c中是如何实现线程同步的?
多线程在各种编程语言中都是难点,很多语言中实现起来很麻烦,objective-c虽然源于c,但其多线程编程却相当简单,可以与java相媲美.这篇文章主要从线程创建与启动.线程的同步与锁.线程的交互.线 ...
- Codevs 3731 寻找道路 2014年 NOIP全国联赛提高组
3731 寻找道路 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在有向图G中,每条边的长度均为1,现给定起点和终点,请你在图中找 ...
- P1417 烹调方案
P1417 烹调方案 题目提供者tinylic 标签 动态规划 难度 普及+/提高 题目背景 由于你的帮助,火星只遭受了最小的损失.但gw懒得重建家园了,就造了一艘飞船飞向遥远的earth星.不过飞船 ...
- Ubuntu系统的安装
在上一篇博客中,我们已经建立了一个“空白”的虚拟Ubuntu镜像,在这篇博客中,我们将介绍如何安装并进入完整的Ubuntu系统. 写在前面:不同版本的系统在安装过程中,有些操作可能会不同,但是其核心步 ...
- IOS 学习笔记 2015-03-27 我理解的OC-代理模式
案例1 KCButton.h // // KCButton.h // Protocol&Block&Category // // Created by Kenshin Cui on 1 ...
- layerX && layerY
转载:https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/layerX UIEvent.layerX 非标准 这个属性是非标准的属性,并且 ...
- linux相关解压命令
ZIP 我们可以使用下列的命令压缩一个目录: # zip -r archive_name.zip directory_to_compress 下面是如果解压一个zip文档: # unzip archi ...
- WebStorm快捷键收集
1.webstorm快捷键: IntelliJ-Idea 的快捷键 Ctrl+/ 或 Ctrl+Shift+/ 注释(// 或者/*…*/ ) Shift+F6 重构-重命名 Ctrl+X 删除行 C ...
- php 接口 implements 使用
主要对类名,类所拥有的方法,以及所传参数起约束和规范做用,感觉跟php abstract 抽象类又有点像. 一,接口的定义和调用 <?php interface face1 { const pa ...