原型

JavaScript三大基础——原型链,闭包,作用域

今天!原型它来了!

原型(object.prototype)是函数function的一个属性,它定义了该构造函数new出来对象的公共属性,new出来的对象可以继承该原型的属性和方法。(原型是该构造函数new出来对象的爹或者说祖先)

构造函数和普通函数的区别只有书写的区别(大驼峰式书写方法:所有单词首字母大写),普通函数用小驼峰书写方法(第一个单词首字母小写,剩下单词首字母大写)。

构造函数new出来的对象实例是相互独立且相同的(同一个模子刻出来的,构造函数就是模子,new出来的对象实例就是工艺品,长得都一样,但是相互独立,是不同的个体

注意!!宇神曾教导我:
(1)函数function是对象,但是对象不是函数!而原型本身也是个对象

(2)当实例对象new出来以后,constructor就只是指向构造函数的指针指向存储构造函数的空间,[[proto]]就是指向原型prototype的指针指向存储原型的空间

(3)[[proto]]是在es规范里的写法,而__ proto__是个别浏览器的实现,但指代的都是指向原型的指针

ps:根据数据来源得知现在基本所有浏览器都可以接受__ proto__这个写法

但是这是一种可能要被废弃的写法,还是早点改吧hhhhhh


使用原型提取构造函数公有属性可以减少new对象时代码的冗余:

//不使用原型时
function Car(color, ownerName){
    /*下面三行就是每new一次对象都要执行一次的用于代码*/
    this.carName = "Honda";
    this.height = "1400";
    this.length = "4900";
    
    this.color = color;
    this.ownerName = ownerName;
}
var a = new Car(red, ZJX);
var b = new Car(black ,Eason);

//使用原型后
Car.prototype = {
    carName = "Honda",
    height = "1400",
    length = "4900",
}
function Car(color, ownerName){
    this.color = color;
    this.ownerName = ownerName;
}
var a = new Car(red, ZJX);
var b = new Car(black ,Eason);

检测方法

isPrototypeOf(原型于…)

作用:检测一个对象是否是另一个对象的原型。或者说一个对象是否被包含在另一个对象的原型链

例子:

(用new创建对象)

function Grand(){
    //一些属性或方法
}
var grand = new Grand;

Father.prototype = grand;
function Father(){
    //一些属性或方法
}
var father = new Father;

Son.prototype = father;
function Son(){
    //一些属性或方法
}
var son = new Son;

Son.prototype.isPrototypeof(son) ---->true
Father.prototype.isPrototypeof(son) ---->true
Grand.prototype.isPrototypeof(son) ---->true
Object.prototype.isPrototypeOf(son) ---->true

(用create创建对象)

var p = {x:1};//定义一个原型对象
var o = Object.create(p);//使用这个原型创建一个对象
//这就没有构造函数的概念了,直接用p去create o,o继承于p
p.isPrototypeOf(o);//=>true: o继承于p
Object.prototype.isPrototypeOf(p);//=> true p继承自Object.prototype

综合上面的两个例子,我们发现在调用isPrototypeOf()的时候有三种方式

p.isPrototypeOf(o);//=>true
Object.prototype.isPrototypeOf(p);
Animal.prototype.isPrototypeOf(eh)//=>true

总结一下就是:
通过Object.create()创建的对象使用第一个参数作为原型
通过对象直接量的对象使用Object.prototype作为原型
通过new创建的对象使用构造函数的prototype属性作为原型

instanceof(实例于…)

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

instanceof运算符希望左操作数是一个对象实例,右操作数是一个构造函数。如果左侧对象是右侧的实例,则表达式返回为true,否则返回false。

// 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
 function Aoo(){}
 function Foo(){}
 Foo.prototype = new Aoo();//JavaScript 原型继承

 var foo = new Foo();
 console.log(foo instanceof Foo)//true
 console.log(foo instanceof Aoo)//true

上面的代码中是判断了一层继承关系中的父类,在多层继承关系中,instanceof 运算符同样适用。

instanceOf与isPrototypeOf简单总结

  • 如果实例A instanceof 构造函数B为True,那么B.prototype.isPrototypeof(A)一定为True(针对于用构造函数new出来产生的原型链)
  • 由于isPrototypeof是prototype上的属性,所以new Object()和Object.create()出来对象的结果在isPrototypeof会有不同。

hasOwnProperty

hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

const object1 = {};
object1.property1 = 42;
console.log(object1.hasOwnProperty('property1'));   // expected output: true
console.log(object1.hasOwnProperty('toString'));    // expected output: false

原型链

注意:Object.prototype是所有对象原型链的终端!!

function Grand(){
    //一些属性或方法
}
var grand = new Grand;

Father.prototype = grand;
function Father(){
    //一些属性或方法
}
var father = new Father;

Son.prototype = father;
function Son(){
    //一些属性或方法
}
var son = new Son;

原型链的增删改只能自身权限增删改,下级(子辈)没有权限增删改,下级的增删改只能增删改自身的(晚辈没权利玩弄祖先的玩意儿,只能看看)

原生JS实现new的过程

/**
 * 
 * @param  fn 构造函数
 * @param  arg 传入的形参
 */
function _mynew(fn, ...args) {
    const t = typeof(fn)
    //判断fn是否为一个函数(先判断为函数再判断是否能被new的函数)
    if(t !== "function"){
        throw new TypeError('${t} must be a function')
    }
    //1. 新建一个对象
    let _this = Object.create(fn.prototype);  //由fn.prototype作为原型去新建一个对象
    //2. 将实例的__proto__指向构造函数的prototype
    Object.setPrototypeOf(_this, fn.prototype);
    //3. 设置实例的constructor
    _this.constructor = fn;
    //4. 通过apply构造函数来传参
    const result = fn.apply (_this,args);
    //5.返回值判断(return值如果为引用值,则改变为其本身)
    if(result&&(typeof result === "object"||typeof result === "function")){
        return result
    }
    return _this;
}

为什么要进行返回值判断?