- prototype原型对象
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性prototype既是属性也是对象(事实上,他是一个指向某个 叫做 “原型对象"的对象 的属性)
这个原型对象的用途就是去包含所有共享的属性和方法。例子是Array(Array是一个构造数组的函数,创建数组时被执行)的slice就存在这里面
简单来说:prototype是一个容器,存着这个函数所有将共享的属性和方法。
值得注意的是 :prototype其实是一个函数对象
- __ proto__隐式原型
js万物皆对象,这些对象都有__ proto__属性,即:对象具有属性__ proto__,可称为隐式原型。
一个对象的隐式原型指向构造该对象的构造函数的原型(下文通过new论证如何实现)这也保证了实例能够访问在构造函数原型中定义的属性和方法。(就像下面自定义属性和方法那样)
__ proto__指向这个实例的构造器的原型即prototype,好比数组的__ proto__就指向了Array的原型,因此我们可以通过给Array的原型添加新的方法(比如切割)来玩弄数组。
为什么只能是Array而非我自己的构造器呢?
因为除非你不用Array这个构造器来建立数组,否则这个数组的__ proto__显然是指向Array这个构造器的原型而非是你的。所以理论上你如果可以用你自己的构造器新建数组的话,那这是ok的
特殊:函数本身的构造器是Function,于是函数的__ proto__指向Function.prototype(写在js内部)
通过constructor(暂不知why) 我们还可继续为原型对象添 加其他属性和方法 (如下方2.1)
- 构造函数:一个返回this对象的函数
构造函数和普通函数的区别在于:调用方式不一样。作用也不一样(构造函数用来新建实例对象)配合new可以实现自动化
构造函数的函数名与类名相同:Person( ) 这个构造函数,Person 既是函数名,也是这个对象的类名
内部用this 来构造属性和方法
构造流程
let Parent = function (name, age) {
//1.创建一个新对象,赋予this,这一步是隐性的,
// let this = {};
//2给this指向的对象赋予构造属性
this.name = name;
this.age = age;
//3.如果没有手动返回对象,则默认返回this指向的这个对象,也是隐性的
// return this;
};
注意:上面的三步走并不是指这个函数会这么执行,而是new的时候帮它执行这三步了
是new帮它return的 当然 你可以手动创建that 然后return
new的过程发生了什么
• 以构造器的prototype属性(即存放公共方法或属性的那个对象)为原型(即__ proto__),创建新对象,即:还是个空对象时,就有了那些公共属性
• 将this(也就是上一句中的新对象)和调用参数传给构造器,执行;
• 如果构造器没有手动返回对象,则返回第一步创建的对象原型链
age是怎么找到的呢,首先会去person1的私有属性(本身的属性),有没有age,如果有的话就去这个属性的值,如果没有就会查找person1.__ proto__指向上的对象(即Person.prototype)有没有该属性有的话就会返回该属性的值,如果没有接着查找person1.__ proto__.__ proto__* 即指向了Object.prototype (因为__ proto__也是由Object构造器构造的嘛)然后看有没有你写的属性或者方法 没有的就返回undefined了。上述的查找过程就是作用域链。
(什么叫'直到Object.prototype指向的对象有没有'*)?
就是一路按相同规则查找就完事了 修改在上面了。
function Person1(){ }
Person1.prototype.name = "Nicholas";
Person1.prototype.age = 29;
Person1.prototype.job = "Software Engineer";
Person1.prototype.sayName = function(){ alert(this.name); };
var Person2 = function(){
this.name = 'pj';
Person1.prototype.name = "jy"; //写外面也行
}
var person1 = new Person1();
person1.sayName(); //"Nicholas"
person1.age; // 29
var person2 = new Person2;//()可有可无
person2.name;//pj 而非jy
- constructor来巩固原型链
原型对象有一个默认属性,叫做constructor,这个属性指回原构造函数。即Person.prototype.constructor===Person
如此 p.constuctor 顺着原型链:找不到这个名字,那就找__ proto__ 而所有除了函数外的对象的__ proto__其实都指向了构造器的prototype(原理上面讲了) 于是,找到了constructor,发现,constructor永远指向这个构造函数本身,于是,就指回了他爸Person
function Person(name) {
this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.constructor === Person) // true
console.log(p.__proto__ === p.constructor.prototype) // true
- 自己实现一切:
- 先熟悉apply是什么
1.apply示例:
<script type="text/javascript">
/*定义一个人类*/
function Person(name,age) {
this.name=name; this.age=age;
}
/*定义一个学生类*/
functionStudent(name,age,grade) {
Person.apply(this,arguments); this.grade=grade;
}
//创建一个学生类
var student=new Student("qian",21,"一年级");
//测试
alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);
//大家可以看到测试结果name:qian age:21 grade:一年级
//学生类里面我没有给name和age属性赋值啊,为什么又存在这两个属性的值呢,这个就是apply的神奇之处.
</script>
分析: Person.apply(this,arguments);
this:在创建对象在这个时候代表的是student这个对象//为什么这时student就是对象了?
arguments:是一个数组,也就是[“qian”,”21”,”一年级”];
也就是通俗一点讲就是:用student去执行Person这个类里面的内容,在Person这个类里面存在this.name等之类的语句,这样就将属性创建到了student对象里面
以黑盒子理解:传一个对象和一堆参数(数组)进去。只要目标函数(Person)是个合格的构造函数,那他就会帮你把这个对象的属性给生成好!
拓展:call也差不多
- 正式开始:
// 构造器函数
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
//执行三步走战略
// 1.以构造器的prototype属性为原型,创建新对象;
let child = Object.create(Parent.prototype);
// 2.将this和调用参数传给构造器执行 利用apply 把child的属性生成好
Parent.apply(child, rest);
// 3.返回第一步的对象
return child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';
//最后检验,与使用new的效果相同
child instanceof Parent//true
child.hasOwnProperty('name')//true
child.hasOwnProperty('age')//true
child.hasOwnProperty('sayName')//false
- constuctor
可以理解为一个标记作用 方便追溯分类构造器
var arr = []
// 恒为真
arr.constructor === Array
var num = 2
// 恒为真
num.constructor === Number
var str = '123'
// 恒为真
str.constructor === String
- instanceof检测Parent的原型是否在child的原型链中
- constructor改变后 不会改变instanceof
child instanceof Parent == true
如何改变instanceof的结果呢?方法如下:
Parent.prototype = {};//覆写原型 使得原型链断掉
child.prototype.__proto__ !== Parent.prototype
- constructor改变后 不会改变instanceof
-
ownProperty用于确定某个属性是在实例上还是在原型上 如果是,返回true - 方法和属性要写在prototype还是构造器里?
- 如果写在构造器:每次构造对象时,这些属性和方法将被完全重新生成并写入该对象(为了实现私有),会耗费对应的内存
- 如果写在prototype里:每次构造对象时,会将__ proto__指过来,从而不用重新生成,也就是说,他是所有对应实例所共享的,无论是person1还是person2访问的都是相同的属性,相同的sayname(),这个过程叫做原型继承,显然,谁都可以访问,就无法私有了