prototype与__proto__的简单理解+js 获取原型的三种方法

在这里插入图片描述

# 原型

## 显式原型 prototype

函数定义在全局作用域,会污染全局作用域的命名空间

而且定义在全局作用域中很不安全,多人开发的时候可能会覆盖到这个函数

- ##### 显式原型 prototype

我们创建的每个函数,解释器都会向函数中添加一个prototype属性 

这个属性对应着一个对象,这个对象就是我们所谓的原型对象

 prototype是函数才有的属性,对象不具有prototype属性,对象具有__proto__属性。

- ##### 如果函数作为普通函数调用,这个原型对象没有任何作用(如:var a=Person() console.log(a.prototype); console.log(a.__proto__);全报错 ) 

- ##### 函数以构造函数形式调用时,每个创建的对象中都会有一个隐含属性

- ##### 原型对象就相当于公共的区域,所有同一个类的实例都可以访问到这个原型对象

- ##### 我们可以将对象中公共的部分统一设置到原型对象中

## 隐式原型 __proto__

每个创建的对象中都会有一个隐含属性

这隐含属性指向该构造函数的原型对象,我们可以通过__proto__来访问该属性

## 查找规则
当我们访问对象的一个属性或者方法的时候,他会在对象自身中找,找到则直接使用,没找到则会去原型中找,找到则使用

 原型对象也是一个对象,所以原型对象中还有原型
 当我们访问对象的一个属性或者方法的时候,
 他会在对象自身中找,找到则直接使用,
 没找到则会去原型中找,找到则使用,
 没找到则会去原型的原型中找,找到则使用
 那是无限套娃吗?
其实是会一直找到object对象的原型,object对象的原型没有原型(prototype),则返回undefined(因为是null,所以返回undefined(方法未被定义))
function Fn() {
        this.say = function () {
          console.log("say");
        };
      }
      Fn.prototype.run = function () {
        console.log("run");
      };
      console.log(Object); //对象的构造函数
      console.dir(Object);  //dir跟log差不多,只不过它可以把一些隐藏的对象展开显示出来
      console.dir(Object.__proto__);  //函数的构造函数,所以对象在原型链上实际是指向函数的
      console.dir(Object.prototype); //对象原型
      console.dir(Fn.prototype); //Fn原型,
      console.dir(Fn.prototype.__proto__); //Fn原型的原型链跟Object.prototype相同
      var fn = new Fn();
      
      console.log(fn.prototype);  //undefined
      // 为null  所以获取属性的时候是undefined
      console.log(fn.__proto__); //与Fn.prototype相同
      console.log(fn.__proto__.__proto__); //与Fn.prototype.__proto__相同
      console.log(fn.__proto__.__proto__.__proto__); // null  相当于原型链一层一层查找
  function Person(name) {
// 内部执行this.prototype={}
    this.name = name;
}
Person.prototype.say = function () {
    console.log("哈哈哈");
};
const p1 = new Person("张三");
// 内部执行this.__proto__ = this.prototype
// 对象的__proto__和构造函数的prototype是等价的
console.log(p1.prototype); //undefined
console.dir(Person.__proto__);  //就是底层的函数对象
console.log(p1.__proto__ === Person.prototype); //true
1. 原型上增加方法
 function Person(){
    this.a=1
  }
  Person.prototype.say = function () {
    console.log("哈哈哈");
}
  var d=new Person()
  console.log(Person.prototype); //{say: ƒ, constructor: ƒ}
  console.dir(Person.__proto__); // 函数的原型对象
  console.log(d.prototype); //undefined
  console.log(d.__proto__); //{say: ƒ, constructor: ƒ}


2.强行改变原型
 function Person(){
    this.a=1
  }

  Person.prototype={
    a:5
  }
  var d=new Person()
  console.log(Person.prototype); //{a: 5}
  console.dir(Person.__proto__); // 函数的原型对象
  console.log(d.prototype); //undefined
  console.log(d.__proto__); //{a: 5}

 
 3.原型上不增加方法
 function Person(){
    this.a=1
  }

  var d=new Person()
  console.log(Person.prototype); //{constructor: ƒ}
  console.dir(Person.__proto__); // 函数的原型对象
  console.log(d.prototype); //undefined
  console.log(d.__proto__); //{constructor: ƒ}


function f(){}

var parent=Object.getPrototypeOf(f)
console.log(f.name); //f
console.log(parent.name); //empty
console.log(typeof f.name); //string  在typeof看来就是一串字符
console.log(typeof eval(f.name)); //function eval方法表示运算后再typeof
console.log(typeof parent.name); //string  在typeof看来就是一串字符
console.log(typeof eval(parent.name));//undefined

a=f.prototype.__proto__   //f的显式原型就是f函数本身,那他的隐式原型就是对象的原型;
b=f.__proto__.__proto__  //f的隐式原型是函数的原型,那函数的隐式原型就是对象的原型;

console.log(f.prototype); //{constructor: ƒ}
console.log(f.__proto__); //函数原型
console.log(f.prototype===f.__proto__); //false
console.log(a===b); //true
console.log(a===Object.prototype); //true

console.dir(Object); //对象不等于对象的显示原型也不等于对象的隐式原型
console.log(Object===Object.prototype); //false
console.dir(Object.__proto__.__proto__); //对象的隐式原型是函数原型,函数的隐式原型是对象的原型
console.log(Object.prototype===Object.__proto__.__proto__);//true

见下图更好理解:(逆着看,因为proto原型链就是倒推)

在这里插入图片描述
中间的函数有两种值,一种是普通函数的proto,那得到的是函数的原型,一种是构造函数那得到的值是指向构造函数:

如下例子
//普通函数
function f(){}
console.log(f.__proto__); //函数原型
console.log(f.__proto__.__proto__);  //对象原型
console.log(f.__proto__.__proto__.__proto__); //null 


//构造函数
 function Person(name) {
    this.name = name;
    this.says=function () {
    console.log("哈哈");
};
}
Person.prototype.say = function () {
    console.log("哈哈哈");
};
const p1 = new Person("张三");
console.log(p1.__proto__); //{say: ƒ, constructor: ƒ}   
console.log(p1.__proto__.__proto__);  //对象原型
console.log(p1.__proto__.__proto__.__proto__); //空
function sxc(){}
var parent=Object.getPrototypeOf(sxc)
console.dir(sxc.__proto__);
console.dir(parent===sxc.__proto__); //true
console.log(parent.name);  //空
console.log(typeof eval(parent.name)); //undefined
console.log(sxc.name); //sxc
console.log(typeof eval(sxc.name)); //function
function sxc(params) { 
    }
    
    var old=sxc.name
    console.log(old); //sxc
    sxc.name="ba" //这样不可能把函数的名字改变
    console.log(sxc); //ƒ sxc(params) {}
    console.log(old,sxc.name); //sxc sxc
  function f(params) {
        
    }
    var a=f.prototype
 b=Object.getPrototypeOf(f)  //Object.getPrototypeOf()方法相当于取f的隐式原型
 var c=f.__proto__
 console.log(c==b); //true
 console.log(c===b); //true
  console.log(a==b); //flase
 console.log(a===b); //flase

var e=function(){}
 var a={}
console.log(a.prototype ); //undefined 函数才有

console.log(e.prototype); //{constructor: ƒ}
console.log(Object.prototype===e.prototype.__proto__); //true
       console.log(Object.prototype==a.__proto__); //true
      var  b=Object.prototype
      console.log(a.prototype===b); //false
      console.log(Object.getPrototypeOf(a)===b); //Object.getPrototypeOf(a)相当于a.__proto__

let a=new Object  //没加括号只可以创建一个空对象
let b=new Object() 
console.log(a); //{}  对象的构造函数
console.log(b); //{} 对象的构造函数
console.log(a===b); //false  对象(引用数据类型)指向不同的内存地址
console.log(a.prototype); //undefined  因为prototype是函数(构造函数形式的调用,也不能是普通函数调用)才有的属性
console.log(b.prototype); undefined
console.log(a.__proto__===b.__proto__); //true
console.log(a.__proto__==Object.prototype); //true



let c=new Object("3")
console.log(c); //String {'3'}
console.log(c.__proto__); //隐式原型是String对象的构造函数
console.log(c.prototype); //undefined  因为prototype是函数(构造函数形式的调用,也不能是普通函数调用)才有的属性

let d=new Object(3)
console.log(d); //Number {3}
console.log(d.__proto__); //隐式原型是Number对象的构造函数
console.log(d.prototype); //undefined

let e=new Object({a:5})
console.log(e); //{a: 5}
console.log(e.prototype); //undefined
console.log(e.__proto__===Object.prototype);  //true
   function Fruts() {

        }
        Fruts.prototype = {
            color: "red",
            say: function () {
                console.log(this);
                console.log(this.color);

            }
        }
        var obj = new Fruts()
        obj.say()   //this指向obj实例,实例的原型上有color,所以this.color=red
        var ba = { color: "ye" }
        obj.say.call(ba)  //this指向{color: 'ye'},所以this.color=ye
        obj.say.apply(ba)  //this指向{color: 'ye'},所以this.color=ye

        obj.say.apply(null)   //指向空,this则是全局对象,全局对象没color,全局对象的原型上更没color,所以this.color=undefined
 function Parent() {
            this.a = 1;
            this.b = [1, 2, this.a];
            this.c = { demo: 5 };
            this.show = function () 
            {
                 console.log(this, this.a, this, this.b, this.c.demo); 
            }
        }
        function Child() {
            this.a = 2;
            this.change = function () 
            { 
                this.b.push(this.a); 
                this.a = this.b.length; 
                this.c.demo = this.a++; //a的值先运算赋值给deom,再
            }
        }
        Child.prototype = new Parent();
        var parent = new Parent();
        var child1 = new Child();
        var child2 = new Child();
        child1.a = 11;
        child2.a = 12;
        parent.show(); //1  [1, 2, 1] 5 
        child1.show(); //11  [1, 2, 1] 5  不管是子元素的元素还是父元素的运算,this始终指向是Child对象,运算时
        //会先在对象本身上找,找到就用,找不到再往原型上找,一层一层(从对象的原型的方法开始运算时,也是先从对象本身找)
        child2.show(); //12  [1, 2, 1] 5
        child1.change();
        child1.show(); //5  [1, 2, 1, 11] 4
     
        child2.change();
        child2.show(); //6  [1, 2, 1, 11,12] 5

        parent.show();//1  [1, 2, 1] 5 

可以以原型继承的方式让b继承a的所有属性和方法:如下

function a(){
    this.name="张三"
}
a.prototype.say=function(){
    console.log(this.name);
}

function b()
{
    this.age=18;
}

console.dir(a.__proto__); //函数的原型
console.log(a.prototype);// {say: ƒ, constructor: ƒ a()}
b.prototype=new a()  //可以以原型继承的方式让b继承a的所有属性和方法
let c=new b()
console.log(c); //b {age: 18}
console.log(c.prototype); //undefined
console.log(c.__proto__); //a {name: '张三'}
console.log(c.__proto__.__proto__);  //{say: ƒ, constructor: ƒ a()}

js 获取原型的方法(三种):

  1. Object.getPrototypeOf()
  2. __proto__
  3. constructor.prototype

例子:

function Person(name){
    this.name=name;
}
var p = new Person("abc");

console.log(Object.getPrototypeOf(p)); //{constructor:  ƒ Person(name)}
console.log(p.__proto__); //{constructor:  ƒ Person(name)}
console.log(Object.getPrototypeOf(p)===p.__proto__);//true  getPrototypeOf就是取隐式原型

console.log(Person.prototype);//{constructor:  ƒ Person(name)}
console.log(p.constructor.prototype); //{constructor:  ƒ Person(name)} //p的构造者的原型就相当于Person的原型
console.log(p.prototype); //undefined   p是通过原型链指向Person对象,没显式原型
console.log(Person.prototype===p.constructor.prototype); //true


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值