原型prototype与原型链__proto__

数据类型新的扩展&很重要的几句话

  • 类——函数类型;实例——对象类型;函数也是对象,也有自己的键值对
  • 每一个函数 (除ES6中的箭头函数外) 都有一个内置属性prototype(原型属性),属性值是一个对象,存储了当前类的公共属性&方法供实例调用。
  • prototype堆中,如果是浏览器为其默认开辟的堆内存,则默认存在constructor属性,属性值为函数本身。言外之意:如果重写了原型对象,则没有constructor属性,为了保证结构的完整性需要手动设置constructor。
  • 每一个对象都内置一个属性__proto__(原型链属性),属性值为当前实例所属类的原型。

原型链机制

以一道题为例,剖析原型与原型链的底层机制:

function Fn(){
	this.x = 100;
	this.y = 200;
	this.getX = function(){
		console.log(this.x);
	}
}
Fn.prototype.getX = function(){
	console.log(this.x);
}
Fn.prototype.getY = function(){
	console.log(this.y);
}
let f1 = new Fn;
let f2 = new Fn;

在这里插入图片描述
调用当前实例对象的某个属性(成员访问),先看是否是自己的私有属性,如果是则返回私有属性的值;如果不是则沿着原型链__proto__向上查找所属类原型中的公有属性/方法,……直到查找到基类Object的原型为止。

f1.__proto__.getY:跳过查找私有的getY方法,直接查找其所属类原型上的方法。IE中不允许使用__proto__属性。
f1.getX(): 涉及到this时,先确定执行的是哪个方法,谁调用的这个方法,方法中的this就是谁。

检测某个属性是否是某个对象的属性

console.log(f1.hasOwnProperty("getY"));   // => false
console.log(Fn.prototype.hasOwnProperty("getY"));   // => true
  • 对象.hasOwnPrototype(“属性”): 检测属性是否是对象中的私有属性
  • 属性 in 对象: 无论是私有属性 / 公有属性,只要其原型链中有这个属性,就返回true
console.log(getY in f1);   // => true
console.log(getY in Fn.prototype);   // => true

new函数执行要不要加()

普通函数:

  • Fn(): 普通函数执行
  • Fn: 普通函数本身

构造函数:无论加不加(),都是将函数执行,并返回一个实例对象

  • new Fn(): 有参数new,可以传递参数;优先级19
  • new Fn: 无参数new;优先级18

基于内置类原型扩展方法

为啥要往原型上扩展方法

  1. 浏览器内置的方法不一定能满足所有需求。
  2. 方便实例调用。实例.方法 => 方法中的this: 该实例

tips

  • 扩展的方法名最好加上前缀my_(或者其他的也ok),防止被替换掉。

    String.prototype.my_func = function my_func(){…}
    
  • this的结果一定是对象类型值。如果向基本数据类型的原型上扩展方法,this会变成它相应的对象类型,但是还是按照原始方法处理即可。因为浏览器默认会调用这个对象的valueOf方法,返回其原始值。

  • 返回结果如果还是当前类的实例,则还可以继续调用当前原型上的方法——链式写法(链式调用)

    // 需求:写方法,实现如下功能
    let n = 10;
    let m = n.plus(10).minus(5);
    console.log(m);
    

    分析:n为数字,它可以调用plus、minus方法,说明这两个方法应该写在Number.prototype上,数字实例才可以调用。该方法为链式写法,说明方法返回的结果还是当前类的实例。

    function handleNum(num){       // 这个方法对传入的num参数进行处理,保证num是数字类型值
    	num = Number(num);
    	return isNaN(num) ? 0 : num;
    }
    Number.prototype.plus = function plus(num){
    	num = handleNum(num);
    	return this + num;
    }
    Number.prototype.minus= function minus(num){
    	num = handleNum(num);
    	return this - num;
    }
    

    this的使用可以保证n一定是数字类型值,否则无法调用Number.prototype上的方法,省去了一些判断步骤。

    为了不使handleNum函数暴露在全局中,我们使用闭包:

    (function (){
    	function handleNum(num){       
    		num = Number(num);
    		return isNaN(num) ? 0 : num;
    	}
    	Number.prototype.plus = function plus(num){
    		num = handleNum(num);
    		return this + num;
    	}
    	Number.prototype.minus= function minus(num){
    		num = handleNum(num);
    		return this - num;
    	}
    })();
    
  • 对于对象,它的属性方法无论是私有的还是公有的,存在枚举特点,即是否可以被for-in循环遍历到:

    • 能遍历到——可枚举
    • 不能遍历到——不可枚举
    • 原型上的内置方法——不可枚举
    • 内置类原型上自己扩展的方法——可枚举
    let obj = {
    	name: "cmj",
    	age: 24
    }
    for(let key in obj){
    	console.log(key);     // name  age
    }
    Object.prototype.AAA = function AAA(){…};
    for(let key in obj){
    	console.log(key);     // name  age  AAA
    }
    

    如果希望忽略原型上的扩展方法 (可能有一些别人扩展到原型上的, 你并不想要),需要这么写:

    for(let key in obj){
    	if(!obj.hasOwnProperty(key))  break;
    	console.log(key);     // name age
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值