Js中 constructor, prototype, __proto__ 详解

Souleigh ✨ 等级 351 0 0

本文为了解决以下问题:

__proto__(实际原型)和prototype(原型属性)不一样!!! constructor属性(原型对象中包含这个属性,实例当中也同样会继承这个属性) prototype属性(constructor.prototype原型对象) __proto__属性(实例指向原型对象的指针)

首先弄清楚几个概念:

什么是对象? 若干属性的集合

什么是原型? 原型是一个对象,其他对象可以通过它实现继承。

哪些对象有原型? 所有的对象在默认情况下都有一个原型,因为原型本身也是对象,所以每个原型自身又有一个原型(只有一种例外,默认的对象原型在原型链的顶端) 任何一个对象都可以成为原型

接下来就是最核心的内容:

constructor 属性

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

    var arr=[1,2,3];
    console.log(arr.constructor); //输出 function Array(){}
    var a={};
    console.log(arr.constructor);//输出 function Object(){}
    var bool=false;
    console.log(bool.constructor);//输出 function Boolean(){}
    var name="hello";
    console.log(name.constructor);//输出 function String(){}
    var sayName=function(){}
    console.log(sayName.constrctor)// 输出 function Function(){}

    //接下来通过构造函数创建instance
    function A(){}
    var a=new A();
    console.log(a.constructor); //输出 function A(){}

以上部分即解释了任何一个对象都有constructor属性,指向创建这个对象的构造函数.

prototype属性

注意:prototype是每个函数对象都具有的属性,被称为原型对象,而proto属性才是每个对象才有的属性。一旦原型对象被赋予属性和方法,那么由相应的构造函数创建的实例会继承prototype上的属性和方法。

    //constructor : A
    //instance : a
    function A(){}
    var a=new A();

    A.prototype.name="xl";
    A.prototype.sayName=function(){
        console.log(this.name);
    }

    console.log(a.name);// "xl"
    a.sayName();// "xl"

    //那么由constructor创建的instance会继承prototype上的属性和方法

constructor属性和prototype属性

每个函数都有prototype属性,而这个prototype的constructor属性会指向这个函数。

    function Person(name){
        this.name=name;
    }
    Person.prototype.sayName=function(){
        console.log(this.name);
    }

    var person=new Person("xl");

    console.log(person.constructor); //输出 function Person(){}
    console.log(Person.prototype.constructor);//输出 function Person(){}
    console.log(Person.constructor); //输出 function Function(){}

如果我们重写(重新定义)这个Person.prototype属性,那么constructor属性的指向就会发生改变了。

    Person.prototype={
        sayName:function(){
            console.log(this.name);
        }
    }

    console.log(person.constructor==Person); //输出 false (这里为什么会输出false后面会讲)
    console.log(Person.constructor==Person); //输出 false

    console.log(Person.prototype.constructor);// 输出 function Object(){}  
    //这里为什么会输出function Object(){}
    //还记得之前说过constructor属性始终指向创建这个对象的构造函数吗?

    Person.prototype={
        sayName:function(){
            console.log(this.name);
        }
    }
    //这里实际上是对原型对象的重写:
    Person.prototype=new Object(){
        sayName:function(){
            console.log(this.name);
        }
    }
    //看到了吧。现在Person.prototype.constructor属性实际上是指向Object的。

    //那么我如何能将constructor属性再次指向Person呢?
    Person.prototype.constructor=Person;

接下来解释为什么,看下面的例子。

    function Person(name){
        this.name = name;
    } 
    var personOne=new Person("xl"); 
    Person.prototype = {
        sayName: function(){
            console.log(this.name);
        }
    };
    var personTwo = new Person('XL');
    console.log(personOne.constructor == Person); //输出true
    console.log(personTwo.constructor == Person); //输出false   
    //大家可能会对这个地方产生疑惑?为何会第二个会输出false,personTwo不也是由Person创建的吗?这个地方应该要输出true啊?
    //这里就涉及到了js里面的原型继承
    //这个地方是因为person实例继承了Person.prototype原型对象的所有的方法和属性,包括constructor属性。当Person.prototype的constructor发生变化的时候,相应的person实例上的constructor属性也会发生变化。所以第二个会输出false;
    //当然第一个是输出true,因为改变构造函数的prototype属性是在personOne被创建出来之后。

proto和prototype属性

    function Person(name){
        this.name=name;
    }
    Person.prototype.sayName=function(){
        console.log(this.name);
    }
    var person=new Person("xl");
    person.sayName(); //输出 "xl"

    //constructor : Person
    //instance : person

首先给构造函数的原型对象Person.prototype赋给sayName方法,由构造函数Person创建的实例person会继承原型对象上的sayName方法。

为什么会继承原型对象的方法?

因为ECMAscript的发明者为了简化这门语言,同时又保持继承性,采用了链式继承的方法。 由constructor创建的每个instance都有个__proto__属性,它指向constructor.prototype。那么constrcutor.prototype上定义的属性和方法都会被instance所继承。

    function Person(name){
        this.name=name;
    }
    Person.prototype.sayName=function(){
        console.log(this.name);
    }

    var personOne=new Person("a");
    var personTwo=new Person("b");

    personOne.sayName(); // 输出  "a"
    personTwo.sayName(); //输出 "b"

    console.log(personOne.__proto__==Person.prototype); // true
    console.log(personTwo.__proto__==Person.prototype); // true

    console.log(personOne.constructor==Person); //true
    console.log(personTwo.constructor==Person); //true
    console.log(Person.prototype.constructor==Person); //true

    console.log(Person.constructor); //function Function(){}
    console.log(Person.__proto__.__proto__);
收藏
评论区

相关推荐

Js中 constructor, prototype, __proto__ 详解
本文为了解决以下问题: __proto__(实际原型)和prototype(原型属性)不一样!!! constructor属性(原型对象中包含这个属性,实例当中也同样会继承这个属性) prototype属性(constructor.prototype原型对象) __proto__属性(实例指向原型对象的指针) <br/ 首先弄清楚几个概念: <br/
初识 JS 中的柯里化
作为函数式编程语言,JS带来了很多语言上的有趣特性,比如柯里化。 1. 简介 柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。 核心思想是把多参数传入的函数拆成单参数(或部分)函
😎手撕吊打面试官系列面试题
js基础 1. 用js打印一个乘法表 这一题面试官考察的是你关于js的打印相关基础api的熟悉程度,以及基本的数学常识,送分题 console.log( 111 212 224 313 326 339 414 428 4312 4416 515 5210 5315 5420 5525
项目实战之---AES 加密
ajax/index.js import axiosApi from '../js/fetch'; import { baseUrl, headerParams } from '../js/baseUrl'; // import引用AES源码js import CryptoJS from 'cryptojs/cryptojs'; console.lo
VUE3(七)vue项目抽离.vue文件中的js、css代码
平常再做开发的时候,一般情况下不会将html,js,css代码写到一个文件中。基本上都会写在各自对应的文件中,然后再引入即可。那么在VUE中我们如何抽离vue文件中的js,与css代码呢? 1:抽离javascriptHome.vue<template <div <div :style"{ padding: '24px', back
js去除字符串
js去除字符串js<DOCTYPE html<html<head <title</title</head<body</body<script type"text/javascript" function delHtmlTag(str){   return str.replace(/<^/g,""); } var s
js删除表格中的某一行
点击表格中的内容,删除某一行正文js代码如下 function removeTd(obj) { obj.parentNode.parentNode.remove();}
日常必备的JS工具函数大全
为元素添加on方法 Element.prototype.on Element.prototype.addEventListener; NodeList.prototype.on function (event, fn) 、 []['forEach'].call(this, function (el) el.on(ev
只听说过CSS in JS,怎么还有JS in CSS?
CSS in JS是一种解决css问题想法的集合,而不是一个指定的库。从CSS in JS的字面意思可以看出,它是将css样式写在JavaScript文件中,而不需要独立出.css、.less之类的文件。将css放在js中使我们更方便的使用js的变量、模块化、treeshaking。还解决了css中的一些问题,譬如:更方便解决基于状态的样式,更容易追溯依赖关
彻底搞懂 JS 中的 prototype、__proto__与constructor
1\. 前言作为一名前端工程师,必须搞懂JS中的prototype、proto与constructor属性,相信很多初学者对这些属性存在许多困惑,容易把它们混淆,本文旨在帮助大家理清它们之间的关系并彻底搞懂它们。这里说明一点,proto属性的两边是各由两个下划线构成(这里为了方便大家看清,在两下划线之间加入了一个空格: proto ,读作“dunder pr
浅谈JS中的递归
一、递归递归(英语:Recursion)在数学与计算机科学中,是指在函数的定义中使用函数自身的方法在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数其核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解一般来说,递归需要有边界条件、递归前进阶段和递归返回阶段。当边界条件不满足时,递归前进;当边界条件满
聊聊js中的数组
前言Hello,大家好,我是Symbol卢,由于年前公司的业务比较紧,也有一段时间没有更新文章了(有几篇文章还没结搞),于是就是在大年初一和初二这两天写了这篇关于js数组的水文。由于内容比较多,打算分成两期进行分享(此文为第一期),如果文章中有不恰当的地方,也欢迎各位大佬(前辈)多多指教,(本人菜的抠脚,文中称为笔者),好了废话不多说,咱们直接进入正文关于数
「JS — 操作」更新中
数组中是否存在某值 languagearr.indexOf('XXX') // 不存在返回1,存在返回该值坐标截取字符串圆括号中的内容 languagevar source 'url(XXX)'var res /(.+)?(?:\(|()(.+)(?\)|))/.exec(source)console.log(res[2]) // XXX字符串转json
前端 - 常见的异常捕获方法
前端异常捕获在ES3之前js代码执行的过程中,一旦出现错误,整个js代码都会停止执行,这样就显的代码非常的不健壮。从ES3开始,js也提供了类似的异常处理机制,从而让js代码变的更健壮,程序执行的过程中出现了异常,也可以让程序具有了一部分的异常恢复能力。js异常的特点是,出现不会导致JS引擎崩溃,最多只会终止当前执行的任务。回归正题,我们该如何在程序异常发生
从 生成器 到 promise+async
本文主要讲解js中关于生成器的相关概念和作用,以及到后面结合 promise 实现 es7中的 async 原理,你将学习到js中异步流程控制相关知识 1、认识生成器思考如下代码:javascript let x 1 function foo() x++ bar() console.log(x) // 3 function bar(