# 原型
## 显式原型 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 获取原型的方法(三种):
Object.getPrototypeOf()
__proto__
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