构造函数、原型、原型链

为了更好了理解构造函数,原型,原型链之间的关系,我们先看什么是对象。

1、 对象是什么?

  • 说到对象不得不说到类。世界万物都可分类,具有相同的特征和行为的归为一类。比如动物类,植物类。动物类又可划分为,单细胞动物、多细胞动物、哺乳动物;哺乳动物又可分为人,猪,狗等等。
  • 动物对象或植物对象所具有的特征就是编程里类的属性,具有的行为就是类的方法。所以说,对象具有类所描述的属性和方法。反过来也可以说类描述了具有相同特征和行为的对象。
  • 在程序中创建一个动物对象或植物对象就是以类作为该对象的基础,因此类是对象的模板、蓝图
  • 人们所研究的事物都可称为对象,比如人这个对象,具有人类共同的特征和行为。在Java中通过Person person = new Person();这段代码我们创建出了一个人对象。由此又可以看出**类的抽象的,而对象是具体的。**类的具体是对象,对象的抽象的类。

小结:

类:就是描述了一组具有相同特征和行为的对象。

对象:是人们研究的任何事物,所以万物皆对象。对象具有类所描述的属性和方法。

类是对象的模板蓝图。类是抽象的,对象是具体的。对象的抽象是类,类的具体化是对象。

在JS中对象是什么?ECMA-262把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。” 简单来说,对象就是一系列的键值对(key-value),而键值对分为两种,属性(property)和方法(method)。

面向对象编程,就是站在对象的角度来思考问题,把任务抽象成一个个对象,赋予对象一些属性和方法,通过各个对象之间的相互协作,以此来完成任务。

面向过程编程,就是站在过程的角度来思考问题,把任务分成一个个步骤,然后用函数实现各个步骤的功能,再在主函数中一次调用这些函数,来完成任务。

2、对象的创建

  • 使用字面量创建对象

    var cat = {
    		name: 'xiaomao',
    		color: 'pink'
    	};
    console.log(cat);
    //output
    //{name: "xiaomao", color: "pink"}
    
  • 利用 new Object 创建对象

    var dog = new Object();
    dog.name="xiaogou";
    dog.color="black";
    console.log(dog);
    //output
    //{name: "xiaogou", color: "black"}
    
  • 利用构造函数创建对象

     function Fish(name, color){
    		this.name=name;
    		this.color=color;
    	}
    var fish = new Fish("xiaoyu", "red");
    console.log(fish);
    //output
    //Fish {name: "xiaoyu", color: "red"}
    

构造函数的特点:

  1. 构造函数约定首字母大写。是为了区别于普通函数
  2. 内部使用this对象,来指向新生成的实例对象
  3. 函数内的属性和方法前面需要添加 this ,表示新对象的属性和方法。
  4. 当我们创建对象的时候,必须用 new 来调用构造函数

3、构造函数

在Java中,类是对象的模板,对象就是类的实例。而在JavaScript中,是利用构造函数(constructor)作为对象的模板,来生成对象的实例。因此可以把构造函数等同于类,都是为对象提供模板来生成实例的。

通过使用字面量创建对象利用 new Object 创建对象 都是以Object构造函数为模板的。而我们利用构造函数创建对象,比如下面的代码就是**以Fish这个构造函数为模板创建的对象的。

function Fish(name, color){
		this.name=name;
		this.color=color;
	}
var fish = new Fish("xiaoyu", "red");
console.log(fish);
//output
//Fish {name: "xiaoyu", color: "red"}

当使用new操作符来调用Fish构造函数创建对象时,经历以下步骤:

  1. 调用构造函数,创建一个新对象
  2. Fish构造函数prototype属性指向空对象的原型对象,这个prototype是属性,是隐式引用,是原型对象。(下面详细介绍)
  3. 将新对象设置为构造函数内部的this关键字,在构造函数中就可以使用this来引用新对象了。
  4. 执行构造函数内部的代码。
  5. 将新对象作为返回值返回。

构造函数的缺点:

  • 在一个已存在的对象构造器中是不能添加新的的属性和方法。
  • 同一个构造函数来创建的实例对象之间不能共享属性和方法。

解决:

创建一个公共对象或者称之为公共容器,来提供共享的属性和方法。这个公共对象就叫做原型。

function Fish(name, color){
    this.name=name;
    this.color=color;
}
Fish.prototype.age=12;
Fish.prototype.breathe=function(){
    console.log(this.name+" 呼吸")
}
var fish1 = new Fish("xiaoyu1", "red");
var fish2 = new Fish("xiaoyu2", "blue");
console.log(fish1.age===fish2.age); //true
console.log(fish1.breathe===fish2.breathe);//true

4、原型是什么

4.1 原型是什么呢?

prototype

object that provides shared properties for other objects

给其他对象提供共享属性的对象

也就是说,原型也是一个对象,只不过承担了给其他对象提供属性的功能而已。原型描述的是两个对象之间的关联(其中一个为另一个提供属性和方法的访问权限)。我们平常说原型对象时,只是简短的描述,实际上说的是 “xxx对象的原型对象”。

所有的对象都有一个隐式引用

Every object has an implicit reference (called the object’s prototype)

所有的对象都有一个隐式引用,这个隐式引用被称之为该对象的 prototype。

什么叫隐式引用?

function Fish(name, color){
    this.name=name;
    this.color=color;
}
var fish = new Fish("xiaoyu", "red");
console.log(fish);

image-20210422172326165

从上面可以看出,我们只定义了name和color属性,却发现还有一个__proto__ 属性。这意味着,Fish对象被隐式地挂载了另一个对象的引用,置于__proto__ 属性中。

4.2 历史问题:__proto__

ECMAScript 规范描述 prototype是一个隐式引用,但之前的一些浏览器,已经私自实现了 __proto__ 这个属性,使得可以通过 fish.__proto__这个显式的属性,等同于被定义为隐式引用的 prototype。即fish.__proto__==Fish.prototype

因此,情况是这样的,ECMAScript 规范说 prototype 应当是一个隐式引用:

  1. 通过Object.getPrototypeOf(obj)间接访问指定对象的原型对象。
  2. 通过Object.setPrototypeOf(obj, anotherObj) 间接设置指定对象的原型对象。
  3. 部分浏览器提前开了__proto__ 的口子,使得可以通过obj.__proto__ 直接访问原型对象,通过obj.__proto__ = anotherObj直接设置原型。
  4. ECMAScript 2015 规范只好向事实低头,将__proto__ 属性纳入了规范的一部分。

4.3 小结

原型就是给其他对象提供共享属性和方法的公共对象。

所有对象都有一个非标准属性__proto__ ,通过该属性可以访问到原型对象。

__proto__ 是每个对象都有的属性,prototype是函数才有的属性。

fish.__proto__等于Fish.prototype,两者都可以访问到原型对象。

4.4 实践

知道了 原型对象、__proto__属性、隐式引用pototype,分析下面代码

function Fish(name, color){
    this.name=name;
    this.color=color;
}
Fish.prototype.age=12;
Fish.prototype.breathe=function(){
    console.log(this.name+" 呼吸")
}

var fish1 = new Fish("xiaoyu1", "red");
var fish2 = new Fish("xiaoyu2", "blue");
console.log(fish1);
console.log(fish2);
console.log(fish1.__proto__===Fish.prototype);//true
console.log(fish1.__proto__===fish2.__proto__);//true
console.log(Fish.prototype.constructor===Fish);//true
image-20210422204241393

constructor属性是原型对象自带的,意味着可以被实例对象继承

console.log(fish1.constructor)//Fish()
console.log(fish1.constructor===Fish.prototype.constructor)//

constructor属性的作用:

  1. 有了constructor属性,可以得知某个实例对象,到底是哪一个构造函数产生的

    console.log(fish1.constructor.name)//Fish
    
  2. 有了constructor属性,就可以从一个实例对象新建另一个实例

    //fish1 是构造函数Fish的实例
    //从fish1,constructor间接调用构造函数
    var fish3 = new fish1.constructor();
    console.log(fish3 instanceof Fish);//true
    

    constructor属性表示原型对象和构造函数的关系,所以修改了原型对象,一般会同时修改construtor的属性,避免引用出错,instanceof失真。

4.5 普通对象和函数对象

JS中万物都是对象,但是对象也分为

  • 普通对象

    • 最普通的对象(Object的实例对象)
    • 原型对象(Fish.prototype 原型对象还有constructor属性(指向构造函数对象))
  • 函数对象:

    • 凡是通过new Function()创建的都是函数对象。

需要注意的是:

  • 所有对象都有__proto__ 属性,函数对象才有prototype属性(prototype是属性,是指针,是隐式引用,是原型对象)
//函数对象  
function F1(){};  
var F2 = function(){};  
  
console.log(typeof F1);  //function  
console.log(typeof F2);  //function  
console.log(typeof Object);   //function  
console.log(typeof Array);   //function  
console.log(typeof String);   //function  
console.log(typeof Date);   //function  
console.log(typeof Function);   //function 

Array是函数对象,是Function的实例对象,Array是通过newFunction创建出来的。因为Array是Function的实例,所以Array.__proto__ === Function.prototype

5、 原型链

image-20210421152538852

最后结合这张图看,就更加清晰明了了!

img

进阶必读:深入理解 JavaScript 原型

我对js原型和原型链的理解 图清晰明了

JS——深入理解构造函数和原型对象

JS重点整理之JS原型链彻底搞清楚

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值