这篇文章主要介绍了 JavaScript 中的实例对象与原型对象, 针对 constructor 属性和 prototype 属性展开来讲, 需要的朋友可以参考下
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
首先声明:javascript 中每个对象都有一个 constructor 属性和一个 prototype 属性。constructor 指向对象的构造函数,prototype 指向使用构造函数创建的对象实例的原型对象。
- function Person() {
- }
- var person = new Person();
- Person.prototype = {
- constructor: Person,
- name: 'zxs',
- age: 24,
- sayName: function() {
- alert(this.name)
- }
- }
- person.sayName();
在这段代码中会报错,sayName() is not defined。根据 javascript 高级程序设计第二版的解释,是因为重写的原型切断了构造函数与最初原型之间的联系。但是我们调整一下上面语句的顺序。如下:
- function Person() {}
- //var person = new Person();
- Person.prototype = {
- constructor: Person,
- name: 'zxs',
- age: 24,
- sayName: function() {
- alert(this.name)
- }
- }
- /*===========================================================*/
- var person = new Person();
- /*===========================================================*/
- person.sayName(); // zxs
- alert(person.constructor) //function Object() { [native code]} or function Person() {} 取决与蓝色的语句是否有效
注意上面两段代码等号中间的语句。按第二段的顺序写代码,将会输出 "zxs",这个结果说明在第一种情况下报错并不能说明是因为切断了构造函数与原想之间的联系引起的。
- Person.prototype = {}
本来就是一种定义对象的方法,而且在 javascript 中每个对象的 constructor 属性都默认的指向 Object 构造函数,这也就不难说明重写原型对象确实切断了构造函数与最初原型之间的联系,但并不能说明这种联系被切断之后 person 就不能访问到 sayName() 函数。
现在有这样的假设:函数的 prototype 属性所指向的原型对象,与我们显示新建的原型对象并不是完全等同的。当我们调用函数的时候会创建一个原型对象,此时会首先查找当前环境中是否存在其原型对象,如果程序中不存在,就创建一个,如果环境中存在,侧查找他们的属性和方法,最后根据查找的结果返回一个原型对象,这个对象中的属性和方法总是优先使用默认原型中的属性和方法,也就是构造函数中定义的属性和方法。当当调用的方法或属性不存在于默认的原型中时,才使用定义在 Person.prototype = {} 的属性和方法。
javascript 是解释性的语言,语句都是顺序执行的,在第一段代码中,当我们使用 new 关键字创建新对象的时候,Person.prototype = {} 并没有执行,也就是说在当前的执行环境中找不到其中定义的方法和属性,而构造函数中没有该方法,所以出错。就像一个变量,给他赋值的时候程序没有执行将不能使用。在第二段中环境中已经存在该调用的方法,构造函数的原型对象已经创建完毕,所以可以得到结果。
再看下面的一段程序:
- //////////////////////////////////////////////////////////////////////////
- function Person() {}
- /*===========================================================*/
- var person = new Person();
- Person.prototype.name = 'song';
- /*===========================================================*/
- //Person.prototype.sayName = function(){alert(this.name)};
- Person.prototype = {
- constructor: Person,
- name: 'zxs',
- age: 24,
- sayName: function() {
- alert(this.name)
- }
- }
- person.sayName(); // error
- //////////////////////////////////////////////////////////////////////////
- function Person() {}
- /*var person = new Person();*/
- Person.prototype.name = 'song';
- /*Person.prototype.sayName = function(){alert(this.name)};*/
- Person.prototype = {
- constructor: Person,
- name: 'zxs',
- age: 24,
- sayName: function() {
- alert(this.name)
- }
- }
- /*===========================================================*/
- var person = new Person();
- /*===========================================================*/
- person.sayName(); // zxs
从这里可以看出使用 Person.prototype.name = '', 的方式不论在什么地方创建对象都能被访问,如果同时存在对象字面量和这种方法定义原型对象,将使用后定义的作为最终值。并且对原型对象使用对象字面量定义之后,该定义必须出现在创建对象的语句之前才能被访问到。
实例不能访问到原型对象中的属性和方法,不仅仅是因为重写原型对象切断了构造函数与最初原型之间的联系。
- function Person() {
- }
- var person = new Person();
- Person.prototype = {
- //constructor : Person,
- name: 'zxs',
- age: 24,
- sayName: function() {
- alert(this.name)
- }
- }
- person.sayName();
以上代码在实例化对象时构造函数的原型为空,它没有任何除默认属性以外的属性。重写构造函数的原型确实切断了构造函数与最初原型之间的联系。
在使用 new 操作符以后构造函数的原型对象中的属性和方法已经添加到 person 对象中。因为以上方法为函数原型添加新属性和方法不具有动态性,所以 person 不能访问到新添加的属性和方法。
重写原型对象之后,就如同如下代码:
- var o = {
- name : 'zxs'
- }
- var obj = o;
- o = {}
- console.log(o.name);
此时输出的值是 undefined,因为,对象是一个引用类型,"=" 是赋值操作符,并且其运算顺序是从右往左。o={} 就是说 o 的指向已经改变,是一个空对象。
Person.prototype.mothed = function() {} 与 Person.prototype={mothed:function(){}} 的区别就如同 arr = [] 和 arr.push() 一样,前者都是修改自身,后者是完全改变自身。
来源: http://www.phperz.com/article/17/0218/266716.html