前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《你不知道的JavaScript》:js构造函数的假面

《你不知道的JavaScript》:js构造函数的假面

作者头像
前端_AWhile
发布2019-08-29 13:12:33
1K0
发布2019-08-29 13:12:33
举报
文章被收录于专栏:前端一会前端一会

本篇继续看下对象的内置属性[[Prototype]]

在js中[[Prototype]]属性最常出现的地方构造函数添加“原型方法”上面了。

代码语言:javascript
复制
1function Foo(name){
2    this.name = name;
3}
4Foo.prototype.showName = function(){
5    return this.name;
6}
7var obj = new Foo("nitx");
8obj.showName();     // nitx

瞧,上面是js中最常见的类构造函数创建新实例对象,然后实例对象调用类的成员方法的过程。

唔,习惯了java之类的面向对象思维的同学一看,没毛病,就是这么个道理,对Foo类的有参构造方法进行new一下,创建出一个复制了Foo成员属性和成员方法的新对象obj。

但是!

js中是不存在类的!

js是基于原型的,面向原型的。

上面这段代码的正确描述应是:普通函数Foo拥有一个所有函数都有的公有且不可枚举的属性[[Prototype]],这个属性的作用就是指向另一个对象,这个对象通常被称为Foo的原型,因为通常是通过名为Foo.prototype的属性引用来访问它。对象a是在调用 new Foo()时创建的,最后会被关联到"Foo点prototype"对象上,也就是对象a会被关联Foo.prototype。注意,是关联哦,不是像类创建对象那样复制出一个新对象那样。用js的方式理解就是,对象a的原型就指向了Foo.prototype对象了,来验证下:

代码语言:javascript
复制
1function Foo(){}
2var obj = new Foo();
3Object.getPrototypeOf(obj) === Foo.prototype;    // true

证明new Foo()创建出来的新对象obj的原型的确指向了Foo.prototype这个对象了。

看到这里,需要仔细思考下上面说的对象obj关联到Foo.prototype对象的意义所在了。正是这个“关联”用词把js的原型和类彻底区分开来。java等面向对象语言中,类实例化是一个复制过程,可以复制多次创建多个新对象,这个复制的过程就是“把类的行为复制到物理对象中”,对于每个实例对象来说都会重复这一过程。

但在js中,却没有这样的重复机制,不能创建一个类的多个实例,只能创建多个对象,它们的内置属性[[Prototype]]指向(关联)的的是同一个对象。但是在默认情况下不会复制,因此这些对象不会完全失去联系,它们是关联的。

new Foo()会创建一个新对象obj,这个新对象obj的内置属性[[Prototype]]关联的是Foo.prototype对象。最后我们就得到两个对象,它们之间相互关联,整个过程就是这样。我们没有初始化一个类,实际上我们都没有从“类”中复制任何行为到一个对象中,只是让两个对象相互关联。

理解了上面的代码的原理,再来回头看下所谓的“构造函数Foo”。它其实不是一个真正意义上的构造函数,因为js中都没有类,就更别提哪来的构造函数了。Foo其实就是一个js最普通的函数罢了,只有当使用 new 关键字来调用函数Foo时,Foo才被称为构造函数,同时为了与普通函数区分,学习了真正的构造函数那样写作首字母大写,以示把它当作构造函数。注意,只是当作,实际这个函数Foo依然是js世界中最普通的一个函数,本质没有改变,只是称呼改变罢了。

除了令人迷惑的“构造函数”之称,还有个容易被搞混的东西constructor,看下面代码:

代码语言:javascript
复制
1function Foo(){};
2Foo.prototype.constructor === Foo;      //true
3var obj = new Foo();
4obj.constructor === Foo;    //true

看上面代码,可以暂时这样理解:Foo.prototype默认有一个公有并且不可枚举的属性constructor,这个属性引用的是对象关联的函数,上例关联的是Foo函数。另外通过”构造函数“调用new Foo()创建的对象obj也有一个属性constructor,指向是是创建这个对象的函数。

但这里注意了,实际上new调用创建的新对象obj默认是没有constructor这个属性的,虽然obj.constructor确实指向了Foo函数,但是它其实是通过原型链委托给了Foo.prototype对象,obj.constructor行为访问的其实是obj对象的原型对象Foo.prototype对象上的属性constructor的值。

代码语言:javascript
复制
1function Foo(){};
2var obj = new Foo();
3console.log(Object.getPrototypeOf(obj) === Foo.prototype);  // true
4console.log(obj.constructor);                        // Foo
5console.log(obj.hasOwnProperty("constructor"));     // fakse
6console.log(Foo.prototype.constructor);             // Foo
7console.log(Foo.prototype.hasOwnProperty("constructor"));       // true

所以上例代码可以看到,obj对象本身确实是没有内置属性constructor,而对obj.constructor的访问其实全是委托给obj对象的原型链上层对象Foo.prototype对象的。

此时可以思考一个问题,看如下代码:

代码语言:javascript
复制
1function Foo(){};
2Foo.prototype = {a:10};
3var obj = new Foo();
4obj.constructor === Foo;    // 此时还是 true么??????

答案是false!因为Foo.prototype = {a:10};这段代码已经把Foo.prototype指向对象{a:2}了,这是一个字面量方法创建的对象,它的原型是Object.prototype。所以此时obj.constructor值为Object:

代码语言:javascript
复制
1obj.constructor === Object;     // true

此时如果想要将obj.constructor的指向重新修改到预期的Foo身上,就需要重新定义Foo.prototype的属性constructor的特性了,用什么方法呢?

代码语言:javascript
复制
 1function Foo(){};
 2Foo.prototype = {a: 10};
 3var obj = new Foo();
 4obj.constructor === Object;     // true
 5Object.defineProperty(Foo.prototype, "constructor", {
 6    value: Foo,
 7    writable: true,
 8    enumerable: false,
 9    configurable: true
10})
11obj.constructor === Foo;       // true

好,这样就完成constructor指向的修改。

下面来总结下本篇所学:

  • js中没有类
  • new Foo()中Foo本质不是传统面向对象语言中类中的构造函数,而是js普通函数
  • 构造函数创建的新对象没有constructor属性,访问它只能通过原型委托进一步访问Foo.prototype对象本身的constructor属性值
  • Foo.prototype对象本身的constructor指向可以修改,所以不推荐在实际开发中通过 obj.constructor来引用Foo函数

-------------------------------- 热门文章 --------------------------------

设计模式>>>

javascript设计模式一: 单例模式

javascript设计模式二:策略模式

javascript设计模式三:代理模式

javascript设计模式四:迭代器模式

javascript设计模式五:原型模式

javascript设计模式六:发布-订阅模式(观察者模式)

javascript设计模式七:模板方法模式

javascript设计模式八:职责链模式

javascript设计模式九:中介者模式

javascript设计模式十:装饰者模式

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档