JavaScript中原型和原型链详解


一、原型

1、原型的定义

在这里插入图片描述

示例:以人为例子,原型就是人的祖先。放在编程中就是你定义了一个Person的构造函数,电脑会自动生成函数Person的对象,这个Person的函数会继承Person对象里面的成员(属性和方法)。

Person.prototype.personName = '张三';
function Person(){}
let p1 = new Person();
let p2 = new Person();
console.log(p1.personName); // 张三
console.log(p2.personName); // 张三
// 批量获取公共祖先类的成员(属性和方法)

如上示例:函数Person中没有定义任何东西,但是通过关键字new一个Person后,对象p使用了属性personName。

补充:函数Person为什么在第二行声明的,第一行Person.prototype.personName = '张三’为什么没有报错?
这是由于函数作用域的声明提升了。要是不理解去看下函数作用域相关内容。

综上我们获得了原型的定义:不管是哪个构造函数被创建,系统都会为他创建一个与之对应的对象,这个对象就是原型,它定义了构造函数制作出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。

2、原型的使用(prototype)

访问原型的语法:构造函数名.prototype。
例如: Person.prototype

3、原型的作用

(1)思考:将方法写在构造函数中,同样是使用new关键字创建出来的Person对象,为啥方法eat并不一样呢?

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.eat = function () {
    console.log(this.name + '喜欢吃饭');
  }
}
var p1 = new Person('张三', 18);
var p2 = new Person('李四', 19);
console.log(p1.eat === p2.eat); // false

缺点:浪费内存空间
(2)思考:将方法提炼到构造函数外面,使用的就是相同的方法了。

function test() {
    console.log(this.name + '喜欢吃饭');
}
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.eat = test;
}
var p1 = new Person('张三', 18);
var p2 = new Person('李四', 19);
console.log(p1.eat === p2.eat); // true

缺点:解决了浪费内存空间的问题,但是会造成全局污染
(3)总结:构造函数中可以重写new出来的对象的属性和方法,那么当属性或方法都为固定值的时候,重写方法势必会造成内存的浪费和性能的消耗,这就凸显了原型的作用。以如下代码的汽车举例,当车的名称,颜色,车高,价格,轮子数量时,假设是同一辆车只有颜色不同的情况下,使用原型的方式会优于直接重写属性创建对象。

// 使用构造函数创建的对象
function Car(name, color, height, price, wheel){
    this.name = name;
    this.color = color;
    this.height = height;
    this.price = price;
    this.wheel = wheel;
}
let c1 = new Car('五菱宏光', '黑色' ,'1.5米', '10w', 4);
let c2 = new Car('五菱宏光', '白色' ,'1.5米', '10w', 4);
console.log(c1.name, c1.color, c1.height, c1.price, c1.wheel); // 五菱宏光 黑色 1.5米 10w 4
console.log(c2.name, c2.color, c2.height, c2.price, c2.wheel); // 五菱宏光 白色 1.5米 10w 4


// 使用原创整合相同的属性后创建的对象
Car.prototype.name = '五菱宏光';
Car.prototype.height = '1.5米';
Car.prototype.price = '10w';
Car.prototype.wheel = 4;
function Car(color){
  this.color = color;
}
let c1 = new Car('黑色');
let c2 = new Car('白色');
console.log(c1.name, c1.color, c1.height, c1.price, c1.wheel); // 五菱宏光 黑色 1.5米 10w 4
console.log(c2.name, c2.color, c2.height, c2.price, c2.wheel); // 五菱宏光 白色 1.5米 10w 4

如上所示,原型的作用就是:利用原型的特点和概念,可以提取共有属性来减少内存的浪费和全局变量的污染。

4、__ proto __

知识点:为什么是双下划线?
答:变量前面加上_说明是私有变量,由于前端没有真正意义上的私有变量,开发中通过这种默契的方式来不调用私有变量或是通过方法返回这个变量来获取值,而“__”(双下划线)代表系统自定义的私有变量。

在这里插入图片描述
如上图所示,定义一个Person的构造函数,通过p1来创建实例对象,实例对象中就有__proto__的值,它是一个对象。
那么能用__proto__来做什么呢?

function Person() {}
var p1 = new Person();
console.log(p1.__proto__ === Person.prototype); // true

上面示例中得到了以下结论

(1)由来:__proto__是除null外每一个子对象都会有的一个属性,指向该对象的原型。
(2)作用:__proto__可以访问原型,但并不存在于Person.prototype中,来自于Object.prototype。

注意:__proto__虽然大部分的浏览器都自带这个属性,但是w3c的标准中是没有的,你可以在学习或者验证一些关系、甚至在开发环境使用它,但是不要在生产环境中使用

5、constructor

在这里插入图片描述
定义:属于原型对象的属性,指向原型对应的构造函数。
在这里插入图片描述

// 一个简单的构造函数
function Person(name,age){
  this.name = name;
  this.age = age;
}
console.log(Person.prototype.constructor === Person); // true

var p1 = new Person('张三', 18);
// 这里并不会报错,虽然p1并没有constructor属性,但是它的原型上Person有这个属性
console.log(p1.constructor); // ƒ Person(name,age){ this.name = name; this.age = age;}

// 关键点:原型是可以被替换的(所以说, 你看到的不一定为真)
Person.prototype = {
    constructor: Person
}
console.log(Person.prototype.constructor === Person); // true

原型是可以被替换的
访问属性没有回去原型上找

二、原型链

1:原型链定义

在这里插入图片描述
如上图,因为每一个对象都有一个原型,原型也是对象,所以原型也有原型,这样就形成了一个链式结构,称为原型链.

2:原型链使用

function Person(name,age){
  this.name = name;
  this.age = age;
}

var p1 = new Person('张三', 18);
console.log(Person.prototype.constructor === Person); // true
console.log(p1.__proto__.__proto__.constructor); // Object
console.log(p1.__proto__.__proto__.__proto__); // null
console.log(p1.__proto__.__proto__ === Object.prototype); // true

对象访问成员的规则:
如果对象自己有那就访问对象自己的,如果对象自己没有那就去访问原型的.
那如果原型也没有呢,那就沿着原型链一直往上找,直到找到为止.
如果找到找到头了还没有找到就报错或者返回undefined.

3:原型链作用

(1)为什么要使用原型链呢?
①为了实现继承,简化代码,实现代码重用!
②只要是原型链这个链条上的内容,都可以被访问和使用到!
(2)使用原型链有什么作用?
避免了代码冗余,公用的属性和方法,可以放到原型对象中,这样,通过该构造函数实例化的所有对象都可以使用该对象的构造函数中的属性和方法,减少了内存占用。

三、原型链的深入了解

1:基本原型链(第二步详解,这里不重复描述了)

ennnnnn,后面的能看懂就看吧,看不懂就算了,别为难自己。

2:内置对象的原型链

内置对象:JavaScript中的对象分为3种:自定义对象、内置对象、浏览器对象,其中前两种是JS基础内容,属于ECMAScript,浏览器对象是JS独有的。

内置对象有Math、Date、数组对象等,这里以Date(日期对象)举例

在这里插入图片描述

var d1 = new Date();
console.log(d1.__proto__ === Date.prototype); // true
console.log(d1.__proto__.__proto__ === Object.prototype); // true
console.log(d1.__proto__.__proto__.__proto__); // null

3:函数作为对象的原型链

函数是一个对象,这个对象是由Function构造函数实例化出来的

(1)函数是一个对象

function test(){
  console.log('我是一个test的函数');
}
test.testMame = '张三';
test.method = function (){
  console.log('我是test函数的方法');
}
console.log(test.testMame); // 张三
test.method(); // 我是test函数的方法

(2)函数作为对象的原型链

在这里插入图片描述

function test(){
  console.log('我是一个test的函数');
}
console.log(test.__proto__.constructor); // Function
console.log(test.__proto__ === Function.prototype); // true
console.log(test.__proto__.__proto__.constructor); // Object
console.log(test.__proto__.__proto__ === Object.prototype); // true
console.log(test.__proto__.__proto__.__proto__); // null

4:构造函数作为对象的原型链

在这里插入图片描述

function Person(name,age){
  this.name = name;
  this.age = age;
}
// 构造函数是由FUnction实例化
console.log(Person.__proto__.constructor); // Function
console.log(Person.__proto__ === Function.prototype); // true
console.log(Person.__proto__.__proto__.constructor); // Object
console.log(Person.__proto__.__proto__ === Object.prototype); // true
console.log(Person.__proto__.__proto__.__proto__); // null
// 调用构造函数实例话人对象
let p1 = new Person('张三', 18);
console.log(p1.__proto__ === Person.prototype); // true
console.log(p1.__proto__.__proto__ === Object.prototype); // true
console.log(p1.__proto__.__proto__.__proto__ ); // null

5:全~原型链

在这里插入图片描述

function Person(name,age){
  this.name = name;
  this.age = age;
}
// 调用构造函数实例话人对象
let p1 = new Person('张三', 18);
console.log(p1.__proto__ === Person.prototype); // true
console.log(p1.__proto__.__proto__ === Object.prototype); // true
console.log(p1.__proto__.__proto__.__proto__ ); // null
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值