一、原型
原型是function对象的一个属性,定义了构造函数制造出来的对象的‘公共祖先’。(js中万物皆对象,原型也是对象)
1.普通对象和函数对象
var test1 = {}/[];
var test2 = new Object();
var test3 = new Test();//test1-3都是普通对象
//Function Object 是通过 New Function()创建的
function Test(){};
var test4 = function(){};
var test5 = new Function('a','console.log(a)');//Test,test4-5都是函数对象
//因为Test,test4-5的根本还是通过Function创建的
2.构造函数
前面章节中讲解过,上面2中function Test( ){ }就是构造函数(大驼峰式命名)
function Car(width,height){//这里Car是构造函数
this.width = width;
this.height = height;
this.say = function (){
console.log("车宽"+this.width+"m","车高"+this.height+"m");
}
}
var car1 = new Car(2,1.5);//car1是Car的实例
car1.say();
console.log(car1.constructor);//因为是car1是Car构造的
//所以car1身上的constructor(构造函数属性)指向构造出它的函数
3.原型对象
每个函数对象身上都有一个prototype(原型)属性,这个属性指向函数的prototype原型对象(即’公共祖先’)
function Car(){};
Car.prototype.width = 2;
Car.prototype.height = 1.5;
/*
Car.prototype = {
width = 2;
height = 1.5;
}//这里就更加清晰, prototype属性指向prototype对象
*/
Car.prototype.say = function (){
console.log("车宽"+this.width+"m","车高"+this.height+"m");
}
var car2 = new Car();//由Car构造函数构造出来的后代
//可以访问它们的'祖先'prototype(原型)对象的属性
car2.say();
car2.height = 2;//可以给自己添加height属性覆盖'祖先'给自己的属性
car2.say();
console.log(Car.prototype.height);//后代给自己添加属性,不会修改祖先的属性
console.log(car2.constructor);//指向构造它的构造函数Car
构造函数上的Car.prototype属性指向的prototype对象(普通对象)。
这里再说下说到的constructor属性:默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Car)
即:Car.prototype.constructor == Car
然后我们又想到了car2.constructor ==Car,他们之间又有什么联系呢?
因为 Car创建的时候,创建了一个它的实例对象并赋值给它的 prototype
var eg = new Car();
Car.prototype = eg;//原型对象(Person.prototype)是 构造函数(Person)的一个实例
4.特殊情况
function Car(){};
var car2 = new Car();
console.log(typeof(Car.prototype));
console.log(typeof(Object.prototype));
console.log(typeof(Function.prototype));
前面说了,原型对象是普通对象,为什么这里typeof(Function)会返回function类型呢?
解释:构造函数创建的时候,创建了一个它的实例对象并赋值给它的 prototype,即:
var eg = new Function();//所以eg是函数对象
Function.prototype = eg;
undefined和null没有原型
二、_ proto _
1._ proto _
JavaScript 在创建对象(普通对象和函数对象)时,都有一个__proto__ 内置属性,该属性指向创建它的构造函数的原型对象。即:
function Car(){};
var car3 = new Car();
console.log(Car.prototype);
console.log(car3.__proto__);
2.隐式创建
和前面章节对象包装类中说的类似
function Student(name,age,sex){
//1.函数体最前端隐式创建空对象this={}
this.name = name,
//2.this = {
// __proto__:Student.prototype指向它自己原型
// name : name
// }将写的属性添加进去
this.age = age,
//this = {
// name : name,
// age : age
//}
this.sex = sex
//...
//3.return this返回出去
}
var student1 = new Student('jimo',18,'male');//所以student1的__proto__指向创建它的构造函数的原型
三、原型链
1.形成原型链
Grand.prototype.lastName = "Jimo";
function Grand(){};
var grand = new Grand();//grand对象继承了祖先的的lastName属性
console.log(grand.lastName);
Father.prototype = grand;//将对象grand赋给Father的原型
function Father(){
this.name = "xuxu";
};
var father = new Father();//father就可以拿到lastName属性和它自己的name属性
console.log(father.lastName,father.name);
Son.prototype = father;//同理将对象father赋给Son的原型
function Son(){
this.hehe = "呵呵";
};
var son = new Son();//son就能拿到lastName、name属性,和自己的hehe属性
console.log(son.lastName,son.name,son.hehe);
像这样通过原型形成链式结构即为原型链(原型链的终端为Object.prototype.__proto__ == null)
2.设置对象的原型
var obj = {
this.name = 'jimo'
}
var obj1 = Object.create(obj);//create()创建一个新对象,被创建的对象继承另一个对象的原型
console.log(obj1.name);//obj1的原型为obj
var obj2 = Object.create(null);//创建一个非常纯净的对象,里面什么都没有
//绝大多数对象最终都会继承自Object.prototype
如果取名和原型链终端的一些属性(方法)重名,然而实现不同的功能,即为方法的重写。
四、call和apply
1.call
function Car(name){
this.name = name;
}
var obj = {};
Car.call(obj,'BMW');
console.log(obj);//obj借用Car的方法,即Car里this.name的this指向了obj
2.apply
function Car(name,width,height){
this.name = name;
this.width = width;
this.height = height;
}
var obj = {};
Car.apply(obj,['BMW',2,1.5]);//与call方法类似,只是apply以数组方式传参
console.log(obj);//obj借用Car的方法,即Car里this.name的this指向了obj
在企业应用开发时,call和apply改变this指向
五、new实现
function Person(name,age){
this.name = name;
this.age = age;
};
Person.prototype.sayName = function(){
console.log(this.name);
}
function myNew(origin,...res){
// 1.以构造器的prototype属性为原型,创建新对象;
let child = Object.create(origin.prototype);
// 2.将this和调用参数传给构造器执行
let result = origin.apply(child,res);
// 3.如果构造器没有手动返回对象,则返回第一步的对象
return Object.prototype.toString.call(result).slice(8, -1) === 'Object' ? result : child;
}
//创建实例,将构造函数Parent与形参作为参数传入
const child = myNew(Person,'jimo',18);
接下来测试一下是否与new的功能类似:
child.sayName();
console.log(child);
console.log(child instanceof Person)//true
console.log(child.hasOwnProperty('name'))//true
console.log(child.hasOwnProperty('age'))//true
console.log(child.hasOwnProperty('sayName'))//false
这里只是一些简单实现,如果想深入了解,可以自行百度哦
六、练习巩固
1.请输出下面构造该对象的构造函数的名
function A(){};
function B(){};
function CreateObj(){
var n = Math.random();
if(n < 0.5){
return new A();
}else{
return new B();
}
}
var obj = CreateObj();
2.请问下面三个会输出什么,为什么
function A(){};
function B(a){
this.a = a;
}
function C(a){
if(a){
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);
3.下面代码会输出什么
function A(){};
A.prototype.say = function(){};
var a1 = new A();
var a2 = new A();
console.log(a1.say === a2.say);
console.log (A.prototype.constructor );
console.log(A.prototype === Function.prototype);
console.log(A.__proto__ === Function.prototype);
console.log(A.__proto__ === Function.__proto__);
console.log(a1.__proto__ === a2.__proto__);
console.log(a1.__proto__ === A.__proto__);
console.log(Function.__proto__ === Object.__proto__);
console.log(Function.prototype.__proto__ === Object.__prototype.__proto__);
console.log (Function.prototype.__proto___ === Object.prototype);
答案下一期揭晓
博主开始运营自己的公众号啦,感兴趣的可以关注“飞羽逐星”微信公众号哦,拿起手机就能阅读感兴趣的博客啦!