JavaScript的原型及原型链总结

一.原型[prototype]

1.函数的prototype属性
  • 并不是所有对象都能有prototype,只有函数才有prototype属性,每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)

  • prototype也是一个对象,而且默认包含一个constructor属性,这个constructor属性指向函数自己

    function Person(){
        this.name='my name';
    }
    var p=new Person();
    //函数的prototype.constructor指向自己
    console.log(Person.prototype.constructor===Person);//true
    
  • 原型对象中有一个属性constructor, 它指向函数对象

    constructor:当一个函数被用来构造一个对象时,那这个函数就被成为constructor

    实例的构造函数属性(constructor)指向构造函数。

    function Person(){
        this.name='my name';
    }
    var p=new Person();
    //函数的prototype.constructor指向自己
    console.log(Person.prototype.constructor===Person);//true
    
    //constructor:当一个函数被用来构造一个对象时,那这个函数就被成为constructor
    console.log(p.constructor);
    // ƒ Person(){
    //     this.name='my name';
    // }
    
    
  • 在这里插入图片描述

  • 给原型对象添加属性(一般都是方法)

    作用: 函数的所有实例对象自动拥有原型中的属性(方法)

    代码示例:

 // 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
    console.log(Date.prototype, typeof Date.prototype)
    function Fun () { }
    console.log(Fun.prototype)  // 默认指向一个Object空对象(没有我们的属性)

    // 原型对象中有一个属性constructor, 它指向函数对象
    console.log(Date.prototype.constructor===Date)
    console.log(Fun.prototype.constructor===Fun)

    //给原型对象添加属性(一般是方法) ===>实例对象可以访问
    Fun.prototype.test = function () { console.log('test()') }
    var fun = new Fun()
    fun.test()
2.显示原型与隐式原型

总结:只有函数才有prototype属性,而_*proto_*属性却是所有对象都有的

  1. 每个构造函数function都有一个prototype,即显式原型(属性)

  2. 每个对象都有一个[__ proto __],可称为隐式原型(属性)

  3. 实例对象的隐式原型的值为其对应构造函数的显式原型的值

在这里插入图片描述

4.总结:

  • 函数的[prototype]属性: 在定义函数时自动添加的, 默认值是一个空Object对象
  • 对象的[__ proto __]属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
  • 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)

5.代码示例:

  //定义构造函数
  function Fn() {
   // 内部默认执行语句: this.prototype = {}
    }
  // 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象
  console.log(Fn.prototype)
  // 2. 每个实例对象都有一个__proto__,可称为隐式原型
  //创建实例对象
  var fn = new Fn()  // 内部默认执行语句: this.__proto__ = Fn.prototype
  console.log(fn.__proto__)
  // 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
  console.log(Fn.prototype===fn.__proto__) // true
  //给原型添加方法
  Fn.prototype.test = function () {
    console.log('test()')
  }
  //通过实例调用原型的方法
  fn.test()
3.constructor

构造函数的原型中包含属性 constructor 指向该构造函数

function User(name) {
    this.name = name;
}
let p1 = new User("张三");
let ls = new p1.constructor("李四");
console.log(ls.name);//李四

以下代码直接设置了构造函数的原型将造成 constructor 丢失

function User(name) {
    this.name = name;
}
User.prototype = {
    //constructor: User,
    show(){
        console.log("hello");
    }
 };

let lisi=new User.prototype.constructor('李四')
let zhangsan=new User('张三')
zhangsan.show()//hello
console.log(lisi.constructor===zhangsan.constructor);//false
lisi.show()//报错:Uncaught TypeError: lisi.show is not a function

解释:

以上代码直接设置了构造函数的原型将造成 constructor 丢失

通过对象字面量给Person.prototype进行赋值时,此时User的prototype发生了改变,User不再指向原来的prototype,而是指向了一个含有show()方法的新的prototype,但此时constructor丢失,新的User.prototype中没有constructor,所以李四会报错,

正确的做法是要保证原型中的 constructor指向构造函数。

在设置User.prototype时,加入代码constructor: User,

function User(name) {
    this.name = name;
}
User.prototype = {
    constructor: User,
    show(){
        console.log("hello");
    }
 };

let lisi=new User.prototype.constructor('李四')
let zhangsan=new User('张三')
zhangsan.show()//hello
console.log(lisi.constructor===zhangsan.constructor);//true
lisi.show()//hello

二.原型链

  1. 原型链:

    ​ 访问一个对象的属性时,

    ​ 先在自身属性中查找,找到返回

    ​ 如果没有, 再沿着[__ proto __]这条链向上查找, 找到返回

    ​ 如果最终没找到, 返回undefined

  2. 实例对象的隐式原型===构造函数的显式原型

  3. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)

  4. 所有函数都是Function的实例(Function是它本身的实例)

    自己是由自己创建的,好像不符合逻辑,但仔细想想,现实世界也有些类似,你是怎么来的,你妈生的,你妈怎么来的,你姥姥生的,……类人猿进化来的,那类人猿从哪来,一直追溯下去……,就是无,(NULL生万物)
    正如《道德经》里所说“无,名天地之始”。

Function.prototype===Function.__proto__

5.Object的原型对象是原型链的尽头

function Fn(){
    this.test1=function(){
         console.log("test1()");
     }
}
Fn.prototype.test2=function(){
      console.log("test2()");
}
var fn=new Fn();

fn.test1();//test1()
fn.test2();//test2()
console.log(fn.toString());//[object Object]
console.log(fn.test3);//undefined
//fn.test3();  -->   fn.test3 is not a function
//解释 test3 与 test3()
//这里的fn.test3相当于定义了一个属性,但它没有使用过,所以是undefined;
//但在后面加(),test3是一个属性,()表示函数调用,test3()是一个函数,所以会报错:fn.test3 is not a function 


console.log("------------------------------------");

//所有函数都是Function的实例(Function是它本身的实例)
console.log(Function.prototype===Function.__proto__);//true

//函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
console.log(Fn.prototype instanceof Object);//true
console.log(Object.prototype instanceof Object);//false
console.log(Function.prototype instanceof Object);//true

//Object的原型对象是原型链的尽头
console.log(Object.prototype.__proto__);//null

在这里插入图片描述

三.原型链的属性问题

1.读取对象的属性值时: 现在自身查找,如果找不到,会自动到原型链中查找

2.设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值

3.方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上

function Fn() { }
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a, fn1) //xxx Fn{},此时属性a在原型链上,不在fn1上

var fn2 = new Fn()
fn2.a = 'yyy'//设置属性值,直接添加在本身
console.log(fn1.a, fn2.a, fn2) //xxx yyy  Fn{a: "yyy"}
        /*
        Fn {a: 'yyy'}
            [[Prototype]]: Object
                a: "xxx"
        
        */
        
        
function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.setName = function (name) {
     this.name = name
}
var p1 = new Person('Tom', 12)
p1.setName('Bob')
console.log(p1)  //Person {name: "Bob", age: 12}

var p2 = new Person('Jack', 12)
p2.setName('Cat')
console.log(p2) //Person {name: "Cat", age: 12}
console.log(p1.__proto__===p2.__proto__) // true   

四.instanceof判断

1.instanceof是如何判断的?

​ 表达式: A instanceof B

​ 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false

​ A是一个实例对象,B是一个构造函数

在这里插入图片描述

解释:

1.构造函数是一个函数对象,是通过Function构造器产生的

所有函数对象proto都指向Function.prototype,它是一个空函数(Empty function)

console.log(Foo.__proto__===Function.prototype);//true
console.log(Foo.constructor);//ƒ Function() { [native code] }
console.log(Function.prototype);//ƒ () { [native code] }
//Function.prototypes是一个空函数

2.原型对象本身是一个普通对象(实例对象),而普通对象的构造函数都是Object

所有的构造器也都是一个普通 JS 对象

//实例对象的隐式原型===构造函数的显式原型
console.log(Foo.prototype.__proto__===Object.prototype);//true

3.所有的构造器都是函数对象,并且Object是一个构造函数,函数对象都是 Function构造产生的

//实例对象的隐式原型===构造函数的显式原型
console.log(Object.__proto__ === Function.prototype);

4.所有函数都是Function的实例(Function是它本身的实例)

Function.prototype===Function.__proto__

5.Function.prototype也是唯一一个typeof XXX.prototypefunctionprototype。其它的构造器的prototype都是一个对象

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Foo.prototype)   // object

举例1:

function Foo() {  }
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true

举例2:

console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true

function Foo() {}
console.log(Object instanceof  Foo) // false

举例3:

function A () {}
A.prototype.n = 1
let b = new A()
A.prototype = { n: 2, m: 3}//这里并不是增加,而是重写
let c = new A()
console.log(b.n, b.m, c.n, c.m) // 1 undefined 2 3

举例4:

function F (){}
Object.prototype.a = function(){
    console.log('a()')
}
Function.prototype.b = function(){
    console.log('b()')
}
//a()定义在Object的原型上,在源头上,无论怎么调用其实都能找到
//总结一句话:只要在Object原型上定义的方法,任何函数和对象都能看到
let f = new F()
f.a() //a()
f.b() //f.b is not a function -->找不到
F.a() //a()
F.b() //b()

console.log(f) //F {}
console.log(Object.prototype) 
console.log(Function.prototype)

在这里插入图片描述

五.相关方法

1.Object.getPrototypeOf()

Object.getPrototypeOf方法返回一个对象的原型。这是获取原型对象的标准方法。

// 空对象的原型是Object.prototype
Object.getPrototypeOf({}) === Object.prototype
// true

// 函数的原型是Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype
// true

// f 为 F 的实例对象,则 f 的原型是 F.prototype
var f = new F();
Object.getPrototypeOf(f) === F.prototype
// true
2.Object.setPrototypeOf()

Object.setPrototypeOf方法可以为现有对象设置原型,返回一个新对象。

Object.setPrototypeOf方法接受两个参数,第一个是现有对象,第二个是原型对象。

var a = {x: 1};
var b = Object.setPrototypeOf({}, a);
// 等同于
// var b = {__proto__: a};

b.x // 1

上面代码中,b对象是Object.setPrototypeOf方法返回的一个新对象。该对象本身为空、原型为a对象,所以b对象可以拿到a对象的所有属性和方法。b对象本身并没有x属性,但是 JavaScript 引擎找到它的原型对象a,然后读取ax属性。

举例2:

let obj = {
  name: "xiaofeixia"
};
let pjy = {
  web: "Pan"
};

//让obj继承hd,即设置obj的原型为hd
Object.setPrototypeOf(obj, pjy);
console.log(obj.web);//Pan
console.log(Object.getPrototypeOf(obj) == pjy); //true
3.Object.create()

该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。

// 原型对象
var A = {
  print: function () {
    console.log('hello');
  }
};

// 实例对象
var B = Object.create(A);
B.print() // hello
B.print === A.print // true

上面代码中,Object.create方法以A对象为原型,生成了B对象。B继承了A的所有属性和方法。这段代码等同于下面的代码。

var A = function () {};
A.prototype = {
 print: function () {
   console.log('hello');
 }
};

var B = new A();
B.print === A.prototype.print // true

可以通过Object.create()方法创建一个极简对象(纯数据字典对象),没有原型(原型为null)

var obj=Object.create(null);
//通过Object.getPrototypeOf来检查它的原型
console.log(Object.getPrototypeOf(obj));//null

Object.create方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。

var obj = Object.create({}, {
  p1: {
    value: 123,
    enumerable: true,
    configurable: true,
    writable: true,
  },
  p2: {
    value: 'abc',
    enumerable: true,
    configurable: true,
    writable: true,
  }
});

// 等同于
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';

举例2:

let Pan = { name: "xiaofeixia" };
let pjy = Object.create(Pan, {
    p1:{
        value: "a person",
        enumerable: true
    }
});
for (const key in pjy) {
    console.log(key);
}
//打印结果
//p1
//name
4.Object.prototype.isPrototypeOf()

对象实例的isPrototypeOf方法,用来判断一个对象是否是另一个对象的原型。

var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);

o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true
123456

上面代码表明,只要某个对象处在原型链上,isPrototypeOf都返回true

Object.prototype.isPrototypeOf({}) // true
Object.prototype.isPrototypeOf([]) // true
Object.prototype.isPrototypeOf(/xyz/) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false
5.Object.prototype.hasOwnProperty()

hasOwnProperty()方法用来判断某个对象是否含有指定的自身属性

obj.hasOwnProperty("属性名");//实例obj是否包含有圆括号中的属性

这个方法会忽略那些从原型链上继承的属性。而使用 for/in 遍历时同时会遍历原型上的属性。

而使用in 可以检测原型链上是否存在属性。

let a = { url: "xiaofeixia" };
let b = { name: "Pan" };
Object.setPrototypeOf(a, b);
console.log("name" in a);//true
console.log(a.hasOwnProperty("name"));//false
console.log(a.hasOwnProperty("url"));//true

in遍历:

使用 for/in 遍历时同时会遍历原型上的属性

let Pan = { name: "xiaofeixia" };
let pjy = Object.create(Pan, {
    p1:{
        value: "a person",
        enumerable: true
    }
});
for (const key in pjy) {
    console.log(key);
}
//打印结果
//p1
//name
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序媛小y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值