资讯专栏INFORMATION COLUMN

javascript中的constructor&&prototype

huaixiaoz / 2397人阅读

摘要:于是退而求其次叫为类的构造函数。如果这个函数被用在创建自定义对象的场景中,我们称这个函数为构造函数。遇到的问题始终指向创建当前对象的构造函数。

Object.constructor,prototype

对象的prototype和constructor是两个重要的属性,他们总是成对出现,提到constructor的地方,不得不涉及到另外一个非常重要的属性prototype,它是js中基于原型继承的一个基础。所谓的成对出现,是因为function的prototype属性指向了一个prototype对象,在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。

function Person(name)   
{   
   this.name=name;   
   this.showMe=function()   
        {   
           alert(this.name);   
        }   
};   
var one=new Person("JavaScript");   
one.showMe();//JavaScript 

很多人见到了久违的new操作符,于是就叫Person为“类”,可是又没有关键字class的出现,觉得叫“类”有点勉强。于是退而求其次叫Person为类的构造函数。这些概念好像都没有错,之所以出现这样的情况,可能是因为大家都学习了传统的面向对象语言(c++,c#,java等),还有一种思维定势吧。为了让javascript也面向对象,要在javascript中找到与传统面向对象语言的影子。可是按照javascript的说法,function定义的这个Person就是一个Object(对象),而且还是一个很特殊的对象,这个使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。
这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性。(这个之前没有注意过这个区别,代码测试下竟然还真是)

function Person(name)   
{   
   this.name=name;   
   this.showMe=function()   
        {   
           alert(this.name);   
        }   
};   
  
var one=new Person("js");   
  
alert(one.prototype)//undefined   
alert(typeof Person.prototype);//object   
alert(Person.prototype.constructor);//function Person(name) {...};

one这个对象竟然没有prototype属性。。。

function Person(name)   
{   
   this.name=name;   
   this.showMe=function()   
        {   
           alert(this.name);   
        }   
};   
  
Person.prototype.from=function()   
{   
  alert("I come from prototype.");   
}   
  
var one=new Person("js");   
  
one.showMe();//js,这个结果正常 
one.from();//I come from prototype.,这个结果有一点奇怪

要解释这个结果就要仔细研究一下new这个操作符了

var one=new Person("js");

这个语句执行的过程可以分成下面的语句:

var one={};   
Person.call(one,"js");  

按照《悟透javascript》书中说的,new形式创建对象的过程实际上可以分为三步:

第一步是建立一个新对象(叫A吧);

第二步将该对象(A)内置的原型对象设置为构造函数(就是Person)prototype 属性引用的那个原型对象;

第三步就是将该对象(A)作为this 参数调用构造函数(就是Person),完成成员设置等初始化工作。

其中第二步中出现了一个新名词就是内置的原型对象__prop__,注意这个新名词跟prototype对象不是一回事,__prop__(图中标记的inobj)就指向了函数Person的prototype对象。在person的prototype对象中出现的任何属性或者函数都可以在one对象中直接使用,这个就是javascript中的原型继承了。示意图如下所示:

每个函数都有一个默认的prototype属性。
如果这个函数被用在创建自定义对象的场景中,我们称这个函数为构造函数。 比如下面一个简单的例子:

// 构造函数
        function Person(name) {
            this.name = name;
        }
        // 定义Person的原型,原型中的属性可以被自定义对象引用
        Person.prototype = {
            getName: function() {
                return this.name;
            }
        }
        var zhang = new Person("ZhangSan");
        console.log(zhang.getName());   // "ZhangSan"

作为类比,我们考虑下JavaScript中的数据类型 - 字符串(String)、数字(Number)、数组(Array)、对象(Object)、日期(Date)等。
我们有理由相信,在JavaScript内部这些类型都是作为构造函数来实现的;
同时对数组操作的很多方法(比如concat、join、push)应该也是在prototype属性中定义的。
实际上,JavaScript所有的固有数据类型都具有只读的prototype属性(因为如果修改了这些类型的prototype属性,则哪些预定义的方法就消失了),但是我们可以向其中添加自己的扩展方法。

Object.constructor遇到prototype的问题

constructor始终指向创建当前对象的构造函数。

        var arr = [1, 56, 34, 12];// 等价于 var foo = new Array(1, 56, 34, 12);
        console.log(arr.constructor === Array); // true
      
        var Foo = function() { };  // 等价于 var foo = new Function();
        console.log(Foo.constructor === Function); // true
        
        // 由构造函数实例化一个obj对象
        var obj = new Foo();
        console.log(obj.constructor === Foo); // true

        // 将上面两段代码合起来,就得到下面的结论
        console.log(obj.constructor.constructor === Function); // true

但是当constructor遇到prototype时,有趣的事情就发生了。 这个现象在我的这篇博客里基于原型创建对象的时候也提到过。链接描述
我们知道每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。如下例所示:

        function Person(name) {
            this.name = name;
        };
        Person.prototype.getName = function() {
            return this.name;
        };
        var p = new Person("ZhangSan");

        console.log(p.constructor === Person);  // true
        console.log(Person.prototype.constructor === Person); // true
        // 将上两行代码合并就得到如下结果
        console.log(p.constructor.prototype.constructor === Person); // true

当时当我们重新定义函数的prototype时(这里不是修改而是覆盖),或者成为原型重写,constructor的行为就有点奇怪了,如下示例:

        function Person(name) {
            this.name = name;
        };
        Person.prototype = {
            getName: function() {
                return this.name;
            }
        };
        var p = new Person("ZhangSan");
        console.log(p.constructor === Person);  // false
        console.log(Person.prototype.constructor === Person); // false
        console.log(p.constructor.prototype.constructor === Person); // false

是因为覆盖Person.prototype时,等价于进行如下代码操作:

Person.prototype = new Object({
            getName: function() {
                return this.name;
            }
        });

而constructor始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即:

function Person(name) {
            this.name = name;
        };
        Person.prototype = {
            getName: function() {
                return this.name;
            }
        };
        var p = new Person("ZhangSan");
        console.log(p.constructor === Object);  // true
        console.log(Person.prototype.constructor === Object); // true
        console.log(p.constructor.prototype.constructor === Object); // true

如何修正过来,只需要重新覆盖Person.prototype.constructor即可:

function Person(name) {
            this.name = name;
        };
        Person.prototype = new Object({
            getName: function() {
                return this.name;
            }
        });
        Person.prototype.constructor = Person;
        var p = new Person("ZhangSan");
        console.log(p.constructor === Person);  // true
        console.log(Person.prototype.constructor === Person); // true
        console.log(p.constructor.prototype.constructor === Person); // true
Object的常用方法

javascript中的一切皆对象,而这些对象的有一个最父层的类就是Object,常用的一些属性方法汇总一下,这些方法在判断上述问题以及其他方面很有用。

Object.constructor //对象的构造函数

Object.hasOwnProperty() //检查对象属性是否被继承

Object.isPrototypeOf() //检查一个对象是否是另外一个对象的原型

Object.propertyIsEnumerable() //是否可以通过for/in 循环看到属性

Object.toLocaleString() //返回对象的本地字符串表示

Object.toString() //定义一个对象的字符串表示

Object.valueOf() //制定对象的原始值

最近从图书馆把那边厚厚的《javascript高级程序设计》借过来了,看看单单事件就能将讲那么厚厚一章,打算搞一个基础知识系列~~求监督和共勉

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86056.html

相关文章

  • 温故js系列(15)-原型&原型链&原型继承

    摘要:给添加属性给的原型对象添加属性原型链在中,每个对象都有一个属性,其保存着的地址就构成了对象的原型链。实例变量实例函数原型链继承有了原型链,就可以借助原型链实现继承。是中唯一一个处理属性但是不查找原型链的函数。 前端学习:教程&开发模块化/规范化/工程化/优化&工具/调试&值得关注的博客/Git&面试-前端资源汇总 欢迎提issues斧正:原型&原型链&原型继承 JavaScript-原...

    Ethan815 评论0 收藏0
  • 图解javascript原型&原型链

    我们在学习javascript时,经常会听到万物皆对象,但是呢,其实万物皆对象的对象也有区别。分为普通对象和函数对象。1.对象分为函数对象和普通对象    通过new Function()创建的对象都是函数对象,其他的都是普通对象。showImg(https://segmentfault.com/img/bVbtWre?w=526&h=252); 2.构造函数而提到new关键字,我们不得不提到构造...

    sutaking 评论0 收藏0
  • 深入JavaScript(一)this & Prototype

    摘要:然而事实上并不是。函数本身也是一个对象,但是给这个对象添加属性并不能影响。一图胜千言作者给出的解决方案,没有麻烦的,没有虚伪的,没有混淆视线的,原型链连接不再赤裸裸。所以是这样的一个函数以为构造函数,为原型。 注意:本文章是个人《You Don’t Know JS》的读书笔记。在看backbone源码的时候看到这么一小段,看上去很小,其实忽略了也没有太大理解的问题。但是不知道为什么,我...

    The question 评论0 收藏0
  • 前端笔记——JS基础(原型&&原型链)

    摘要:基础原型原型链构造函数默认有这一行张三李四构造函数扩展其实是的语法糖其实是的语法糖其实是使用判断一个函数是否是一个变量的构造函数原型规则和示例所有的引用类型数组对象函数,都具有对象属性即可自有扩展的属性,除外所有的引用类型数组对象函数, JavaScript基础 —— 原型&&原型链 构造函数 function Foo(name, age) { this.name = na...

    n7then 评论0 收藏0
  • 探索 proto & prototype 与继承之间的关系

    摘要:而和的存在就是为了建立这种子类与父类间的联系。创建一个基本对象建立新对象与原型我把它理解为类之间的连接执行构造函数小结可以理解为类,也就是存储一类事物的基本信息。原型原型链和继承之间的关系。 原型 原型的背景 首先,你应该知道javascript是一门面向对象语言。 是对象,就具有继承性。 继承性,就是子类自动共享父类的数据结构和方法机制。 而prototype 和 __proto__...

    dockerclub 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<