Javascript 面向对象编程(一):封装

时间:2023-02-11 20:36:55

转载自:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html


阮一峰的网络日志 » 首页 » 档案 Javascript 面向对象编程(一):封装

分类

 

Javascript 面向对象编程(一):封装

作者: 阮一峰

日期: 2010年5月17日

学习Javascript,最难的地方是什么?

我觉得,Object(对象)最难。因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握。

下面就是我的学习笔记,希望对大家学习这个部分有所帮助。我主要参考了以下两本书籍:

Javascript 面向对象编程(一):封装

《面向对象的Javascript》(Object-Oriented JavaScript)

Javascript 面向对象编程(一):封装

《Javascript高级程序设计(第二版)》(Professional JavaScript for Web Developers, 2nd Edition)

它们都是非常优秀的Javascript读物,推荐阅读。

笔记分成三部分。今天的第一部分是讨论"封装"(Encapsulation),后面的第二部分第三部分讨论"继承"(Inheritance)。

============================

Javascript 面向对象编程(一):封装

作者:阮一峰

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

那么,如果我们要把"属性"(property)和"方法"(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢?

一、 生成实例对象的原始模式

假定我们把猫看成一个对象,它有"名字"和"颜色"两个属性。

  var Cat = {

    name : '',

    color : ''

  } 

现在,我们需要根据这个原型对象的规格(schema),生成两个实例对象。

  var cat1 = {}; // 创建一个空对象

    cat1.name = "大毛"; // 按照原型对象的属性赋值

    cat1.color = "黄色";

  var cat2 = {};

    cat2.name = "二毛";

    cat2.color = "黑色";

好了,这就是最简单的封装了,把两个属性封装在一个对象里面。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。

二、 原始模式的改进

我们可以写一个函数,解决代码重复的问题。

  function Cat(name,color) {

    return {

      name:name,

      color:color

    }

  }

然后生成实例对象,就等于是在调用函数:

  var cat1 = Cat("大毛","黄色");

  var cat2 = Cat("二毛","黑色");

这种方法的问题依然是,cat1cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。

三、 构造函数模式

为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。

所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

比如,猫的原型对象现在可以这样写,

  function Cat(name,color){

    this.name=name;

    this.color=color;

  }

我们现在就可以生成实例对象了。

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat("二毛","黑色");

  alert(cat1.name); // 大毛

  alert(cat1.color); // 黄色

这时cat1cat2会自动含有一个constructor属性,指向它们的构造函数。

  alert(cat1.constructor == Cat); //true

  alert(cat2.constructor == Cat); //true

Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。

  alert(cat1 instanceof Cat); //true

  alert(cat2 instanceof Cat); //true

四、构造函数模式的问题

构造函数方法很好用,但是存在一个浪费内存的问题。

请看,我们现在为Cat对象添加一个不变的属性type(种类),再添加一个方法eat(吃)。那么,原型对象Cat就变成了下面这样:

  function Cat(name,color){

    this.name = name;

    this.color = color;

    this.type = "猫科动物";

    this.eat = function(){alert("吃老鼠");};

  }

还是采用同样的方法,生成实例:

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat ("二毛","黑色");

  alert(cat1.type); // 猫科动物

  cat1.eat(); // 吃老鼠

表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

  alert(cat1.eat == cat2.eat); //false

能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。

五、 Prototype模式

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

  function Cat(name,color){

    this.name = name;

    this.color = color;

  }

  Cat.prototype.type = "猫科动物";

  Cat.prototype.eat = function(){alert("吃老鼠")};

然后,生成实例。

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat("二毛","黑色");

  alert(cat1.type); // 猫科动物

  cat1.eat(); // 吃老鼠

这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。

  alert(cat1.eat == cat2.eat); //true

六、 Prototype模式的验证方法

为了配合prototype属性,Javascript定义了一些辅助方法,帮助我们使用它。,

6.1 isPrototypeOf()

这个方法用来判断,某个proptotype对象和某个实例之间的关系。

  alert(Cat.prototype.isPrototypeOf(cat1)); //true

  alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

  alert(cat1.hasOwnProperty("name")); // true

  alert(cat1.hasOwnProperty("type")); // false

6.3 in运算符

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

  alert("name" in cat1); // true

  alert("type" in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

  for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

未完,请继续阅读这个系列的第二部分《构造函数的继承》和第三部分《非构造函数的继承》

(完)

文档信息

Javascript 面向对象编程(一):封装

Javascript 面向对象编程(一):封装

相关文章

广告(购买广告位)

Javascript 面向对象编程(一):封装
Javascript 面向对象编程(一):封装
Javascript 面向对象编程(一):封装
Javascript 面向对象编程(一):封装

留言(123条)

直白易懂!逐步深入~这篇对于我这种初学者很有用啊!

2010年5月17日 18:24 | # | 引用

崇拜中。。阮兄知识面让我情何以堪啊。。。

2010年5月17日 18:36 | # | 引用

不错的note,最近我在看pro Javascript Design Patterns这本书,三、四章的封装、继承这块讲的非常好

2010年5月17日 21:12 | # | 引用

《javascript: the good part》中Dauglas不推荐用new的这种方法构造对象,因为如果忘记加上new,“即没有编译时警告,也没有运行时警告”。他推荐的是函数化的方法,不使用prototype。

2010年5月18日 07:46 | # | 引用

好多年不写程序了,不过还是觉得javascript是挺复杂的。

2010年5月18日 08:44 | # | 引用

喜欢这篇。
越来越能看出,今后,这种娓娓道来的知识描述形式,将把至今为止的,逻辑严谨机械正确但却难懂的知识描述方式打入历史的垃圾箱里。

人,是有灵性的,是非线性的,是量子性的。
至今为止所谓的“线性严谨二元逻辑性的学术描述方式”,只不过是一种违反人类本质天性的东西,必将在完成其历史使命之后,退出历史舞台。

2010年5月18日 08:59 | # | 引用

引用fan的发言:

Dauglas不推荐用new的这种方法构造对象,因为如果忘记加上new,“即没有编译时警告,也没有运行时警告”。他推荐的是函数化的方法,不使用prototype。

虽然这个意见是正确的。但是,Douglas提出的方法,需要自己写一个函数,在函数里再使用prototype,我觉得很不符合直觉。

2010年5月18日 09:24 | # | 引用

很简洁实用的文章,里面有不少重来没有看过的用法,如:alert("name" in cat1); 
没想到还能用 in 这样做判断

2010年5月18日 10:18 | # | 引用

非常实用易懂的文章!
最近看《Object Oriented JavaScript》,看完第六章讲12种对象封装构造的方法,彻底晕菜。。看了您的文章决定继续去读完这本书,的确是本很好的JS参考书啊

2010年5月18日 11:16 | # | 引用

这么一段代码,ruan兄猜猜是啥结果?我觉得这是封装中最容易犯的错误
var a=function(){
//empty
}

a.prototype.var1=[1,2,3];

var b=new a();
b.var1.push(4);

var c=new a();
alert(c.var1.join(","))

2010年5月18日 11:19 | # | 引用

引用axu的发言:

这么一段代码,ruan兄猜猜是啥结果?我觉得这是封装中最容易犯的错误

所以只能把不变的属性和方法,绑在prototype对象上,而不能把可变属性绑上去。

2010年5月18日 13:31 | # | 引用

我始终觉得javascript是一门函数式编程语言,这种OO的封装应该只是一种临时的workaround

2010年5月18日 18:21 | # | 引用

这时所有实例的type属性和eat()方法,其实都是一个内存地址,指向prototype对象,因此就提高了运行效率。

也增大了风险,因为一个地方改变了,其他地方都变了。和他带来的好处相比,风险更大,不应该推荐。

ruan兄应该是有点“完美主义”的偏执吧

2010年5月19日 10:51 | # | 引用

会不会连载,如果连载的话,就跟着你学了!

2010年5月19日 11:51 | # | 引用

引用fan的发言:

《javascript: the good part》中Dauglas不推荐用new的这种方法构造对象,因为如果忘记加上new,“即没有编译时警告,也没有运行时警告”。他推荐的是函数化的方法,不使用prototype。

这个“如果“有点苍白

2010年5月21日 15:51 | # | 引用

js的水很深很深 特别是用js尝试模仿oo的风格

还是喜欢使用简单的 名称空间+函数

2010年5月21日 17:20 | # | 引用

引用ethantsien的发言:

这个“如果“有点苍白

苍白与否暂且搁置,为了避免忘记加 new,最好的办法是类名首字母大写。

2010年5月24日 09:50 | # | 引用

不错!是一篇好文章!

2010年6月29日 12:17 | # | 引用

这篇文章绝对要顶,学js多年,像中国唯一一个能把问题讲的这么直白透彻的。

2010年7月 2日 12:59 | # | 引用

这篇文章分析得十分仔细清楚,绝对经典啊!!!!

2010年11月11日 10:11 | # | 引用

引用axu的发言:

这么一段代码,ruan兄猜猜是啥结果?我觉得这是封装中最容易犯的错误
var a=function(){
//empty
}

a.prototype.var1=[1,2,3];

var b=new a();
b.var1.push(4);

var c=new a();
alert(c.var1.join(","))



这就涉及到对象的继承机制了. 更直白一点, 涉及到 JS 语言的变量赋值与引用的区分. 

2011年1月12日 10:35 | # | 引用

var parent = function(name){
var that = {};
that.name = name;
return that;
};

var child = function(name,age){
var that = parent(name);
that.age = age;
return that;
};

函数化不难

2011年6月 1日 10:16 | # | 引用

Ruan兄, 文章易懂,不错,Mark~~~!

2011年6月 9日 11:46 | # | 引用

第五点应该是构造函数+原型 的混合模式吧。

2011年6月20日 09:54 | # | 引用

简单易懂,对像我一样的初学者来说很好

2011年6月21日 17:03 | # | 引用

这么入门级的文章,被这么多人“捧”,真够汗的~~

2011年6月24日 11:33 | # | 引用


好文章,深得深入浅出的精髓

2011年7月 1日 13:28 | # | 引用

我个人认为:javascript 只是脚本语言,它和c++或java等语言所肩负的任务不同, 因此关于“继承”等功能不是重点,无需太复杂。
我不赞同依靠javascript大量创建UI界面,构建UI的基础应该是html+css。
当然javascript也可以写得很复杂和高明,那是高手们喜欢做的事。但不代表非要这样不可。
现在的电脑设备速度越来越快,内存越来越大,无需太计较,简单易懂、安全(prototype的做法易出错,风险大,使用时要很小心。)才是重点。

2011年7月10日 13:18 | # | 引用

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

不明白为什么这么说,没有class就不是真正的面向对象?面向对象是一种编程思想,有没有class只是一种对这种思想的具体实现与体现,但没有class也未必不是面向对象。


ecmascript规范里如是说:
ECMAScript is an object-oriented programming language for performing computations and manipulating computational objects within a host environment.

----Ecmascript262 v5 page 1

2011年7月20日 16:28 | # | 引用

初学者看了您的文章受益匪浅,已经购买推荐的书籍正在学习中.感谢...

2011年7月25日 14:48 | # | 引用

第5部分里的 alert(cat1.eat == cat2.eat); 是否改为===更恰当些?

2011年7月29日 15:49 | # | 引用

看了几个星期的书,还不如看着几个例子,浅显易懂!

2011年8月 2日 15:44 | # | 引用

OOP的人看JS真的是很变恋,你说Cat.prototype.type = "猫科动物";这个东西看上去像模仿类的静态方法吧,但访问时又不是直接通过类而是通过类的实例cat1.type;。这让初学编程的人是学面向过程还是面向对向啊?

2011年11月29日 17:36 | # | 引用

引用Yoya的发言:
OOP的人看JS真的是很变恋,你说Cat.prototype.type = "猫科动物";这个东西看上去像模仿类的静态方法吧,但访问时又不是直接通过类而是通过类的实例cat1.type;。这让初学编程的人是学面向过程还是面向对向啊?

访问时通过类或是通过类的实例不能作为判断是否面向对象的依据,其实都是访问一块公共内存而已

2011年12月25日 21:35 | # | 引用

我的问题跟Yoya的一样 ,OOP的人看JS真的是很变恋,你说Cat.prototype.type = "猫科动物";这个东西看上去像模仿类的静态方法吧,但访问时又不是直接通过类而是通过类的实例cat1.type;搞不清楚到底是不是共享的了

2011年12月28日 09:48 | # | 引用

循序渐进 适合学习

2012年2月14日 12:31 | # | 引用

function Cat(name,color){
    this.name = name;
    this.color = color;
  }
  Cat.prototype.type = "猫科动物";
  Cat.prototype.eat = function(){alert("吃老鼠")};

var cat1 = new Cat("大毛","黄色");
  var cat2 = new Cat ("二毛","黑色");
  alert(cat1.type); // 猫科动物

这里两个对象的type显然不是一个内存地址,可以写一个代码跑一下啊,我试了,不再同一内存地址,如果在同一内存地址不就成了静态变量。

2012年2月20日 16:51 | # | 引用

引用Dada的发言:

function Cat(name,color){
    this.name = name;
    this.color = color;
  }
  Cat.prototype.type = "猫科动物";
  Cat.prototype.eat = function(){alert("吃老鼠")};

var cat1 = new Cat("大毛","黄色");
  var cat2 = new Cat ("二毛","黑色");
  alert(cat1.type); // 猫科动物

这里两个对象的type显然不是一个内存地址,可以写一个代码跑一下啊,我试了,不再同一内存地址,如果在同一内存地址不就成了静态变量。

刚才我说的也不准确,下面是摘自一个博客,我感觉挺准确的,你文章里写的有点歧义,容易让人以为prototype中所定的属性是静态方法。

话说每一个方法对象被创建时,都会自动的拥有一个叫 prototype 的属性。这个属性并无什么特别之处,它和其他的属性一样可以访问,可以赋值。不过当我们用 new 关键字来创建一个对象的时候,prototype 就起作用了:它的值(也是一个对象)所包含的所有属性,都会被复制到新创建的那个对象上去。

2012年2月20日 17:15 | # | 引用

我昨天的留言,感觉太草率了,今天又读了一些,发现博主说的还是比较准确的,虽然给我造成了一定的误解,不过博主说还是对的,我上面说的那个根本不正确。。。。

2012年2月21日 14:43 | # | 引用

阮大哥,你这篇刚刚写出来的时候我就看过了,当时看了半天没看懂,直到今天才看懂,我是个超级初级入门者,入门了两年啊,哈哈哈,我实在是太笨了。

谢谢你,终于看明白了,而且原来是那么的浅显易懂啊

2012年2月22日 10:06 | # | 引用

阮老大,javascript现在面相对象编程是一个大趋势啊。我搜遍了国内的网上书店都没有买到将关于javascript面相对象编程的书籍。国内的专家写的什么垃圾书,在就过时了。javascript面相对象的时代到来了。封住继承是必须的了。求javascript面相对象的书啊。。网上就您的这几篇稿子经典,跪添了。

2012年2月28日 16:01 | # | 引用

引用Azrael的发言:

我的问题跟Yoya的一样 ,OOP的人看JS真的是很变恋,你说Cat.prototype.type = "猫科动物";这个东西看上去像模仿类的静态方法吧,但访问时又不是直接通过类而是通过类的实例cat1.type;搞不清楚到底是不是共享的了

所以,我是不喜欢这个风格。
特别是,这种风格带来的如果只是内存节约的话

2012年3月 7日 10:58 | # | 引用

文章非常好,加深理解!!!感谢作者!

2012年3月15日 11:14 | # | 引用

引用Ruan YiFeng的发言:

所以只能把不变的属性和方法,绑在prototype对象上,而不能把可变属性绑上去。



从原型拿来的东西,只是能查询,检索到,更新不会影响到原型的值。这样理解好些?

2012年5月21日 23:13 | # | 引用

很好懂,谢谢啦!

2012年7月11日 21:42 | # | 引用

引用ethantsien的发言:

这个“如果“有点苍白

苍白+1

2012年7月12日 10:06 | # | 引用

Cat.prototype.isPrototypeOf(cat1)//true——原来构造函数的原型才是与实例的原型一致,也就是说原型一致
Cat.constructor == cat1.constructor //false——不知道为什么构造函数与构造函数为什么不一致?
Cat == cat1.constructor //true

2012年7月17日 11:39 | # | 引用

Cat和cat1都有两个子对象:constructor和prototype
cat1.constructor就是Cat
cat1.prototype就是Cat.prototype
问题是,Cat的另一个子对象Cat.constructor是什么呢?

2012年8月11日 23:57 | # | 引用

[QUOTE]
我们现在就可以生成实例对象了。
  var cat1 = new Cat("大毛","黄色");
  var cat2 = new Cat("二毛","黑色");
  alert(cat1.name); // 大毛
  alert(cat1.color); // 黄色
这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。
  alert(cat1.constructor == Cat); //true
  alert(cat2.constructor == Cat); //true
[/QUOTE]
----------------------------------
cat1和cat2自身是没有constructor属性的,而是通过原型链找到Cat.prototype.constructor属性

2012年9月 6日 08:41 | # | 引用

 alert(cat1.eat == cat2.eat); //false

为什么这一句我测试的结果是true?

2012年9月22日 16:13 | # | 引用

阮哥 前面都看的懂了 但是最后一句不太明白
for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }
希望你能解释一下

2012年9月25日 17:54 | # | 引用

果然是你写的.....

2012年12月 3日 21:17 | # | 引用

我擦,评论好多见解是错的。。。把原型链先搞清楚就不会错了

2013年3月 5日 13:03 | # | 引用

Prototype模式 还有一点没有说到,
className.Prototype.method(){
//这里不能使用构造里的私有属性
}

2013年5月14日 12:00 | # | 引用

Prototype模式虽然能减少内存使用,提高应用性能,但是会使代码变得冗长,增加维护难度。更好的做法应该是使用“继承”的方法,将相同的属性和方法封装在另外的对象中。

2013年7月 2日 19:09 | # | 引用

console.log(cat1.type == cat2.type); 和 console.log(cat1.type === cat2.type);
返回的都是 true,求解惑

2013年7月19日 11:59 | # | 引用

终于懂了prototype了

2013年7月25日 10:39 | # | 引用

引用eagle的发言:

 alert(cat1.eat == cat2.eat); //false

为什么这一句我测试的结果是true?

alert(cat1.eat == cat2.eat); //false
alert(cat1.eat() == cat2.eat()); //true

2013年8月25日 23:35 | # | 引用

真的不错的文章,经久不衰啊,这是我看过的最好的一篇讲解这个的

2013年8月30日 18:40 | # | 引用

引用雅蠛蝶的发言:

alert(cat1.eat == cat2.eat); //false
alert(cat1.eat() == cat2.eat()); //true

可否这么理解: alert(cat1.eat == cat2.eat);比较的是方法本身,因为方法所属的对象不同,所以两者不相等; alert(cat1.eat() == cat2.eat());比较的是两个方法的返回值,所以两者相等。

2013年9月27日 10:02 | # | 引用

刚才试了一下发现,prototype方式添加的属性在new 出来的对象中是可以修改的而且不影响别的对象和对象原型,貌似每个new出来的对象都存在prototype里面的属性,那用prototype方式添加的属性和直接在函数里面添加的属性有什么区别了。。。求解

2013年10月15日 15:07 | # | 引用

引用Ruan YiFeng的发言:

所以只能把不变的属性和方法,绑在prototype对象上,而不能把可变属性绑上去。


虽然是10年的文章,但看起来还是有很多启发。但是关于这段代码,窃以为不是属性可不可变的问题,而是,因为[1,2,3]是一个对象,所以var1属性也只是对这个对象的一个地址引用,所以任何实例对象改变这个对象时,其它对象再去访问该属性,都是通过引用找到的内容,因此拿到的都是改变后的属性值。如果var1赋值为基本类型,则不会出现这个问题。

2014年2月25日 13:00 | # | 引用

回头再看的时候,发现很容易就理解的,简单明了!

2014年3月17日 11:44 | # | 引用

引用axu的发言:

这么一段代码,ruan兄猜猜是啥结果?我觉得这是封装中最容易犯的错误
var a=function(){
//empty
}

a.prototype.var1=[1,2,3];

var b=new a();
b.var1.push(4);

var c=new a();
alert(c.var1.join(","))


今天重新看了阮老师写的文章,感觉收获很大,每次在不同的水平下看,总有不同体会,看到一条有意思的评论,上面这个人的在实例化后c居然可以改变a中的数组,按理不应这样,求解答这是什么原理?

2014年3月19日 18:09 | # | 引用

之前也一直是看到 JavaScript 面向对象这块真是头大了,其实很多地方看起来很好理解,但是如果用自己的语言去组织这一部分内容,又什么都记不得了。如果一开始先开看简单浅显的文章,然后在去看这部分就很连贯了。

2014年4月 1日 14:16 | # | 引用

prototype有一个注意点,如果是方法,可以共用内存,如果是一个像name这样的属性,还是会新开内存的。

2014年7月 3日 14:42 | # | 引用

简单明了,确是好文呀。

2014年7月13日 22:17 | # | 引用

赞,ruanyifeng老师果然是入门级指导的大师~

2014年7月17日 05:30 | # | 引用

略显简单吧

2014年8月21日 11:10 | # | 引用

不太理解 函数 和 函数相等 是空间相等 还是弹出的 值相等?

2014年8月27日 15:16 | # | 引用

简单明白,终于知道js的对象了

2014年11月28日 22:52 | # | 引用

讲到很清楚,个人看法是,属性绑定在this上面,方法绑定在prototype上面。

2014年12月 9日 21:33 | # | 引用

我们组长推荐的你的文章,果然不错,浅显易懂,又不缺乏内涵,赞一个!

2014年12月27日 11:14 | # | 引用

个人感觉,阮老师的博客受众是非常非常初级入门的程序员,或者说,应该是邻家大哥这种类型的,能帮一个不懂的孩子入门,或者产生兴趣,这个阶段不能深究晦涩的原理性知识。
主要是因为文字里面有大量的原理性错误,软件公司的员工或者硕士以上学历的朋友建议还是带着批判的眼光去学习比较好。
但是不得不承认,阮老师的文字功底很好,就像有个人在你旁边跟你聊天一样,容易让新手进入状态,这方面必须赞。

2015年1月21日 12:19 | # | 引用

引用owen的发言:

今天重新看了阮老师写的文章,感觉收获很大,每次在不同的水平下看,总有不同体会,看到一条有意思的评论,上面这个人的在实例化后c居然可以改变a中的数组,按理不应这样,求解答这是什么原理?

var1是a原型是的数组 b修改了原型导致var1成了[1,2,3,4] c继承下来了,不是c可以改变a中的数组

2015年1月23日 15:39 | # | 引用

引用redspear的发言:

prototype有一个注意点,如果是方法,可以共用内存,如果是一个像name这样的属性,还是会新开内存的。

其实,关键还是javascript这门语言入门太简单了,所以后面很多人都按自己的理解去做了,都不去看ECMA了,包括我,也包括阮老师!

重新回头看看语言权威的描述,根本没有这些疑问,人家都说的很明白。

2015年1月26日 16:37 | # | 引用

你写的这个不就是那个javascript高级编程里面的么(没错,就是你那参考资料),也没有太大差别。其实这些 在javascript高级编程第3版写的都很详细。

2015年2月 1日 10:02 | # | 引用

对于一个跨行学编程的小白来说,您的这篇文章对我太有意义了!

2015年2月11日 10:54 | # | 引用

写的简洁有力 通俗易懂 牛!

2015年4月16日 20:27 | # | 引用

入门级的,很适合初学者,简单易懂!

2015年6月 2日 16:53 | # | 引用

好文,能提供一个支付宝捐款链接吗?我想donate!

2015年6月17日 04:50 | # | 引用

老师,您的这段代码应该在name 和 color后面加上分号。很好的文章,让我一下子就理解了

/*二、 原始模式的改进
我们可以写一个函数,解决代码重复的问题。
  function Cat(name,color){
    return {
      name:name,
      color:color
    }
  }
*/

2015年7月 6日 12:13 | # | 引用

很棒的文章,感谢,茅塞顿开,看了你的文章再去看这个书更容易理解书了,因为书好晦涩

2015年7月14日 12:03 | # | 引用

深入浅出。

2015年7月23日 10:07 | # | 引用

好文章,太通俗易懂了!

2015年8月 6日 15:27 | # | 引用

  function Cat(name,color){

    this.name = name;

    this.color = color;

  }

  Cat.prototype.type = "猫科动物";

  Cat.prototype.eat = function(){alert(name)}; 
如果在eat方法中用Cat中的name啊

2015年10月 9日 14:56 | # | 引用

写的真好 浅显易懂

2015年11月11日 11:25 | # | 引用

引用richard的发言:

这时所有实例的type属性和eat()方法,其实都是一个内存地址,指向prototype对象,因此就提高了运行效率。

也增大了风险,因为一个地方改变了,其他地方都变了。和他带来的好处相比,风险更大,不应该推荐。

ruan兄应该是有点“完美主义”的偏执吧

所以说要把不变的属性和方便绑在prototype对象上,可变的单独封装,作为私有属性或方法。

2015年11月12日 15:42 | # | 引用

写的真好 浅显易懂

2015年11月20日 16:48 | # | 引用

引用owen的发言:

今天重新看了阮老师写的文章,感觉收获很大,每次在不同的水平下看,总有不同体会,看到一条有意思的评论,上面这个人的在实例化后c居然可以改变a中的数组,按理不应这样,求解答这是什么原理?

这个貌似不是改变原a中的数组吧,而是数组的join方法会重新生成一个新的string。我也是初学者,不知道这样说对不对!

2015年12月15日 17:29 | # | 引用

写js也有一年多了~对于面向对象还是一知半解,没有投入过真正的使用里面,一直搞不明白,有什么建议吗,ruan大哥

2015年12月27日 20:26 | # | 引用

不太同意阮老师的标题“封装”,封装(encapsulation)又叫隐藏实现(Hiding the implementation)。就是只公开代码单元的对外接口,而隐藏其具体实现。我们脑子里面很容易想到的是类似java里面的Public, Protected, Private 等访问控制符,来控制成员变量的访问权限,但是这篇文章只是在讲创建一个类型,而不是讲怎么控制属性的访问权限。

2016年1月 4日 15:17 | # | 引用

写得太好了,把复杂的东西简单讲明白了!
请允许我永久收藏。

2016年1月 7日 12:35 | # | 引用

写的好,作为后端开发。对js知识了解皮毛。最近接触到js的面向对象,看到这篇文章,感觉很有兴趣

2016年1月20日 09:30 | # | 引用

封装的意思不是这个吧?
封装,即隐藏对象的属性和实现细节,仅对外公开接口 这才对吧

2016年1月24日 22:50 | # | 引用

非常适合入门!多谢分享!

2016年2月23日 10:55 | # | 引用

浅显易懂,而且逐步深化,非常感谢!

2016年3月 1日 00:10 | # | 引用

我写了一篇关于Javascript中Prototype的文章,希望阮老师多多指教

2016年3月 3日 15:26 | # | 引用

引用laoguo的发言:

喜欢这篇。
越来越能看出,今后,这种娓娓道来的知识描述形式,将把至今为止的,逻辑严谨机械正确但却难懂的知识描述方式打入历史的垃圾箱里。

人,是有灵性的,是非线性的,是量子性的。
至今为止所谓的“线性严谨二元逻辑性的学术描述方式”,只不过是一种违反人类本质天性的东西,必将在完成其历史使命之后,退出历史舞台。

爲了通俗而通俗,爲了容易理解而理解,只會使得讀者同樣流於表面,自以爲理解,實際上根本不懂。一旦難度稍微加大,舊有的思維不再好用,就難以提升了。

反之一開始就接受抽象思維的訓練而不是套用舊有的思維,雖然一開始艱難,而後必會突飛猛進。

建議这位朋友不要看任何低水平的「科普」文章,直接看英文維基百科、MDN、ECMAScript 草案比較好。

真正的入門往往就是在一瞬間,過了這道坎,你就入門了。盲目地看再多低水平的文章,照樣毫無幫助。

2016年5月24日 20:35 | # | 引用

很喜欢,谢谢写出这么好的文章

2016年5月26日 11:18 | # | 引用

在网上找了很多相关的内容,阮老师说的真的很好,很好理解,非常感谢阮老师分享!

2016年5月27日 19:02 | # | 引用

很好的文章,阮兄真的深入浅出。期待js方面更深的东西

2016年6月12日 10:54 | # | 引用

真的是言简意赅,我这种菜鸟都能理解 ,感谢分享

2016年6月29日 11:24 | # | 引用

6.1 isPrototypeOf()
这个方法用来判断,某个“proptotype”对象和某个实例之间的关系。这个单词写错了。

2016年7月12日 09:29 | # | 引用

引用Ruan YiFeng的发言:

所以只能把不变的属性和方法,绑在prototype对象上,而不能把可变属性绑上去。



var a=function(){
//empty
}
a.prototype.var1=[1,2,3];
a.prototype.var2 = "猫科动物";
var b=new a();
var d = new a();
b.var1.push(4);
b.var2 = "昆虫";
var c=new a();
console.log(b.var2);
console.log(c.var2);
console.log(d.var2);
console.log("--------------");
console.log(c.var1.join(","));
console.log(d.var1.join(","));

不太理解,为什么var1影响了后续实例的对象,var2没有影响到呢?

2016年7月12日 18:15 | # | 引用

@爱喝白开水:

b.var1.push(4);b.var2 = "昆虫"; 主要在这两句的区别,js寻址的规则是先在对象的本地属性下去找,如果找不到在去该对象的proto(不同浏览器的实现可能不一样)中去找,proto指向的是a的prototype。

第一句b.var1.push(4);首先在b的本地属性(也就是上文提出的hasOwnProperty相关)找,没找到,再去b的proto中去找,找到了,然后push一下,这里b.var1和a.prototype.var1引用的是同一个,修改了b.var1中的内容,也相当于修改了a.prototype.var1的内容。

b.var2 = "昆虫"; 这里不是改变了b.var2,而是新定义了b的本地属性var2,你可以用hasOwnProperty判断下,这里的var2是b的本地属性。

2016年8月22日 14:56 | # | 引用

@爱喝白开水:

某个实例改变var1,var2都不会改变之后实例的值
那么var1怎么从[1,2,3]变成[1,2,3,4]了呢?
这里说的"改变"是指"指针(引用)"的改变
不能改变指针的值 但能改变指针的值的值
而var1的指针确实没变 都指向这个数组(指针的值)
只不过是这个数组改变了(指针的值的值)

a.prototype.var1 = [1,2,3,4];(修改指针的值,不生效)
b.var1.push(4);(修改指针的值的值,生效)


不知道你明白没 同学生 正在学JS

2016年8月22日 15:46 | # | 引用

太棒啦,解答我不少的困惑。
另外请教一下:Cat.prototype.type = "猫科动物";改写成var type = "猫科动物";应该也是指向统一内存空间吧???

2016年8月27日 02:23 | # | 引用

老师把高级程序设计中的内容总结起来非常好,让我很快就理解了

2016年9月11日 09:16 | # | 引用

厉害啊!浅显易懂!

2016年11月22日 18:39 | # | 引用

一下看懂了,表白老师( ̄3 ̄)

2016年11月29日 11:17 | # | 引用

对我这样的小白来说很有用,啃不懂大部头,但是还得使用js做一些东西。这篇文章讲解的很清楚。

2016年12月 3日 11:37 | # | 引用

对提高自我的技术修养很是有用!

2016年12月10日 12:32 | # | 引用

比老师讲的易理解

2016年12月20日 15:40 | # | 引用

阮老师真是我学习的榜样,很耐心。

2017年2月10日 21:14 | # | 引用

真的是通俗易懂

2017年3月 3日 10:41 | # | 引用

好文,豁然开朗的感觉

2017年3月 8日 14:17 | # | 引用

其实在看这篇文章前我就懂这一块了,但是还是想过来看看峰峰大神是如何讲解原型对象和构造函数的,没想到峰峰大神讲的如此通俗易懂,膜拜大神!!!

2017年3月13日 15:04 | # | 引用

讲的很明白,谢谢大神!

2017年3月22日 17:15 | # | 引用

引用redspear的发言:

prototype有一个注意点,如果是方法,可以共用内存,如果是一个像name这样的属性,还是会新开内存的。

这个主要是自有属性和继承属性之间的来源差异

2017年4月12日 09:45 | # | 引用

茅塞顿开 只能说很幸运看到这篇文章

2017年5月11日 22:00 | # | 引用

很好理解,如果阮兄能用图表示下每个内存块里面的东西就更好啦

2017年6月 8日 09:33 | # | 引用

通俗易懂 受益匪浅

2017年6月16日 14:03 | # | 引用

我要发表看法

 «-必填

 «-必填,不公开

 «-我信任你,不会填写广告链接

 «- 点击按钮

联系方式 | 2003 - 2017