javascript 原型和原型链

什么是原型?

简单来说,可以通过newfunction语法结构来实例一个对象的,就可以称呼为原型。

// 例如
const arr = new Array(); // Array是原型,arr是实例对象
const obj = new Object(); // Object是原型,obj是实例对象
const Fn = function(){}; // function关键字,Function是原型,Fn是实例对象
const arr1 = []; // 语法结构,原型是Array
const obj = {}; // 语法结构,原型是Object
  • javascript内置原型有:Object、Array、Function、Map、Set、Number、String、Boolean

什么是原型链?

  • 每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
  • 几乎所有 JavaScript 中的对象都是位于原型链顶端的Object的实例。
  • 以上引用于《MDN-继承与原型链》

PS:上面的描述看不懂没关系,看下面的就行了

首先我们先明确 3 个概念:

  • 原型是指:Object、Array、Function……等等
  • 原型对象是指:Object.prototype、Array.prototype、Function.prototype……等等
  • 每个实例对象有一个__proto__属性会指向对应的原型对象

比如:

	const obj = {};  // obj是实例对象。其原型是Object
	obj.__proto__ === Object.prototype; // true

一条完整原型链的构成:

  • 原型链:实例对象 —> __proto__ —> 原型对象 —> …… —> __proto__ —> Object原型对象 —> __proto__ —> null
__proto__
__proto__
__proto__
__proto__
实例对象
原型对象
……
Object原型对象
null

请看以下例子:

Object原型链:实例对象obj —> __proto__ —> Object原型对象 —> __proto__ —> null

__proto__
__proto__
实例对象obj
Object原型对象
null
const obj = {};
console.log('obj的爸爸原型对象 === Object原型对象?',obj.__proto__ === Object.prototype);
console.log('obj的爷爷原型对象',obj.__proto__.__proto__);
// 打印结果:
// obj的爸爸原型对象 === Object原型对象? true
// obj的爷爷原型对象 null

数组原型链:实例对象arr —> __proto__ —> Array原型对象 —> __proto__ —> Object原型对象 —> __proto__ —> null

__proto__
__proto__
__proto__
实例对象arr
Array原型对象
Object原型对象
null
const arr = [];
console.log('arr的爸爸原型对象 === Array原型对象?',arr.__proto__ === Array.prototype);
console.log('arr的爷爷原型对象 === Object原型对象?',arr.__proto__.__proto__ === Object.prototype);

// 打印输出结果:
// arr的爸爸原型对象 === Array原型对象? true
// arr的爷爷原型对象 === Object原型对象? true

普通函数原型链:实例对象fn—> __proto__ —> Function原型对象 —> __proto__ —> Object原型对象 —> __proto__ —> null

__proto__
__proto__
__proto__
实例对象fn
Function原型对象
Object原型对象
null
const fn = function(){};
console.log('fn的爸爸原型对象 === Function原型对象?',fn.__proto__ === Function.prototype);
console.log('fn的爷爷原型对象 === Object原型对象?',fn.__proto__.__proto__ === Object.prototype);

// 打印输出结果:
// fn的爸爸原型对象 === Function原型对象? true
// fn的爷爷原型对象 === Object原型对象? true

构造函数原型链:实例对象newFn—> __proto__ —> Fn原型对象 —> __proto__ —> Object原型对象 —> __proto__ —> null

__proto__
__proto__
__proto__
实例对象newFn
Function原型对象
Object原型对象
null
const Fn = function(){}; 
const newFn = new Fn();
console.log('newFn的爸爸原型对象 === Function原型对象?',newFn.__proto__ === Function.prototype);
console.log('newFn的爸爸原型对象 === Fn原型对象?',newFn.__proto__ === Fn.prototype);
console.log('newFn的爷爷原型对象 === Object原型对象?',fn.__proto__.__proto__ === Object.prototype);

// 打印输出结果:
// newFn的爸爸原型对象 === Function原型对象? false
// newFn的爸爸原型对象 === Fn原型对象? true
// newFn的爷爷原型对象 === Object原型对象? true

PS:构造函数其实就是一个普通的函数,当通过new去实例对象时,普通函数就可以称呼为构造函数。

以上例子可以看得出来:

  • Object原型对象都是处于几条原型链的最终环,而且Object原型对象的__proto__已经是null了,代表了没有再上一级原型了。
  • 这意味着javascript中Object原型对象处于原型链顶端

PS:其他的例子我就不列出来了,建议各位童鞋可以自己去实践一下。

原型链的特点

javascript中当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。《MDN-继承与原型链-基于原型链的继承》

下面我们来看相关的例子:

const Product = function(){}
const keyboard = new Product();
console.log(keyboard.name); // undefined
Product.prototype.name = '键盘';// 增加Product的原型对象属性
console.log(keyboard.name); // 键盘

可以看到原先keyboard.nameundefined的,因为实例对象keyboard本身并不存在name属性。
而当我们为keyboard的原型对象Product.prototype增加name属性后,keyboard.name就有值了。
可见当访问对象一个属性的时候,当对象本身不存在此属性时,则会向上级原型搜索。

那么根据这个特性,我突然想到一个问题,关于 for in 遍历,接下来实践一下:

const Product = function(){
	this.name = '键盘';
	this.price = 99.9;
}
Product.prototype.weight = '500克';
const keyboard = new Product();

for(let key in keyboard){
	console.log(key, keyboard[key]);
}

// 打印结果:
// name 键盘
// price 99.9
// weight 500克

由此可以判断,使用 for in 遍历对象属性时,除了会把对象本身可枚举属性进行遍历外,还会在其原型链上面寻找可枚举属性。这样子就可以解释为啥会说 for in 遍历的性能比较差了。

那么关于这 for in 的问题话,也会引发其他一系列的问题,比如

// A.JS
Object.prototype.type = 'type';

// B.JS
const obj = { id:1, name:'name' };
const newObj = {};
// 获取obj中非id的其他属性
for(let key in obj){
	if(key !== 'id'){
		newObje[key] = obj[key];
	}
}
console.log(newObj);
// 预期效果:{name:'name'}
// 打印结果:{ name:'name', type:'type' };
  • 在扩展了 Object.prototype 后使用了 for in ,可能会出现无法预估的问题,导致后续可能存在的逻辑错误
  • 所以建议不要扩展Object.prototype 或其他内置原型
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值