原型
原型是function对象的一个属性,它定义了构造函数制造出的对象的祖先。通过该构造函数构造出来的对象,可以继承该原型的属性和方法。原型也是对象
原型知识讲解
举一个小栗子
Person.prototype.lastName="deng";
function Person(name,sex){
this.name=name;
this.sex=sex;
}
var person = new Person("xiaoliu",'male');
prototype的讲解
通过上面的代码我们不难看出,对象可以访问到访问自己的属性
然而令人感到好奇的是,通过person这个对象可以访问到Person.prototype上的属性——其实这是因为Person.prototype是person对象的原型(就好像是祖先一样,子孙可以继承祖先的属性和方法),这就是person对象有lastName属性的原因。
增删改查讲解
-
增
无论是原型还是实例对象都使用统一的方式增加属性或者方法
- 删
Person.prototype.lastName="deng";
function Person(name,sex){
this.name=name;
this.sex=sex;
}
var person = new Person("xiaoliu",'male');
毫无疑问,实例对象可以删除自己的属性
实例对象删除原型上的属性返回true,但是没有删除成功,这是为什么呢?看下面演示
删除对象上没有的属性都会返回ture
结论
实例对象可以删除自己的属性,原型可以删除自己的属性,实例对象不能删除原型上的属性。至于原型是否可以删除实例对象的属性——原型上是没有实例对象的属性的
- 查
两种方法
- 改
第一种情况
成功修改原型上的属性以及实例对象的属性
由实例对象修改原型中的属性时,只能成功修改实例对象上的属性,原型中的属性并没有被修改
第二种情况
结果同上一个情况一样
这是你会发现此次修改也只是修改原型上的属性,实例对象上的属性并没有被修改成功,这是为什么呢??——这是因为实例对象已经拥有自己的显式属性,所以修改原型上的属性并不会影响实例对象的属性
由于讲解需要,先把__proto __讲了
proto用于查看原型的一个属性,而且是隐式属性
栗子栗子
Person.prototype.name = 'Sunny';
function Person() {}
var person = new Person();
浅紫色表示隐式属性,深紫色表示显式属性
无论是实例对象上的proto还是原型上的proto属性最终的原型都是Object,Object的原型是null,person.proto==Person.prototype
修改原型上属性的两种方式以及它们的不同之处
- 第一种
Person.prototype.name = 'Sunny';
function Person() {}
Person.prototype.name="Cherry";
var person = new Person();
Person.prototype.name = 'Sunny';
function Person() {}
var person = new Person();
Person.prototype.name="Cherry";
显而易见,上面这两个栗子只是原型修改一个属性
- 第二种 稍微变换一下
Person.prototype.name='sunny';
function Person() {
/*内部是这样子的*/
//var this={
//__proto__:Person.prototype
//}
var person = new Person();
Person.prototype={
name:'cherry'
}
这个栗子先创建了一个实例对象,然后通过不同的方式修改Person.prototype中的name属性,猜一猜Person.prototype.name和person.name的值是什么?
实例对象和原型(也是一个对象),它们拥有对象的特点——存储的是数据的引用,当两个对象是同一个引用时,修改一个对象的引用值时,不会影响另外一个对象的值。现在person.proto和Person.prototype指向同一个空间,然后Person.prototype换一个空间,而原来的person.proto没有变,所以person.name还是为sunny,再举一个更简单的栗子吧~
var obj = {
name: 'a'
};
var obj1 = obj;
obj = {
name: 'b'
}
现在应该懂了修改引用中的值的妙处了吧~
考一考你
Person.prototype.name = 'Sunny';
function Person() {
/*内部是这样子的*/
//var this={
//__proto__:Person.prototype
//}}
Person.prototype = {
name: 'cherry'
}
var person = new Person();
想想预编译过程以及函数的执行顺序(什么时候才调用构造函数)就很容易得出结果了~
constructor讲解
construcor可以查看一个对象的构造函数,构造器是一个隐式属性
一个栗子引入
Person.prototype.name='sunny';
function Person() { }
var person = new Person();
而且constructor可以被修改
function Car(){}
Person.prototype.name='sunny';
function Person() {
//var this ={
//constructor:Person
//}
}
var person = new Person();
person.constructor=Car;
原型链
//Grand.prototype.__proto__->Object.prototype
//Object.prototype.__proto__->null
Grand.prototype.lastName = "deng";
function Grand() {}
var grand = new Grand();
Father.prototype=grand;
function Father() {}
var father = new Father();
Son.prototype=father;
function Son() {}
var son = new Son();
son实例对象查找lastname属性时会根据原型链一层层往上找,直到找到为止,或者找不到返回undefined
原型链上的增删改查
原型链上的增删改查和原型的增删改查是差不多的
- 这里讲一个修改的小特例
在father身上加一个引用值
function Father() {
this.fortune={
fo1:"visa"
}
}
var father = new Father();
Son.prototype=father;
function Son() {}
var son = new Son();
这时通过son修改fortune
上面两种方式不同,第一种son直接修改person.fortune,很显然,只修改了son.fortune的值,而第二种方式是直接操作fortune引用值,显然是引用值增加一个属性,只是引用值的修改。
补充一个知识
var obj = {}
var obj1=new Object();
//obj1._proto_ -->Object.prototype
第一种var obj = {}是对象自变量的创建形式,上面的两种创建方式是一样的,用var obj={}的方式系统会默认用new Object()的方式创建,平时再构造对象时建议用对象自变量的创建方式,即var obj={}
new的作用
1、创建一个新的对象
2、将构造函数的this指向这个新对象
3、返回这个新对象
Object.create()
创建对象的方法
var obj = Object.create(原型)
var obj= {name:"sunny",age:123};
var obj1=Object.create(obj);
obj1的原型就是obj,所以obj1继承obj的属性
再举一个例子
Person.prototype.name="sunny";
function Person(){
}
var person=Object.create(Person.prototype);
和
Person.prototype.name="sunny";
function Person(){
}
var person=new Person();
的结果是一样的,但是在person的构造函数中定义自己的属性就不一样了
一个错误的概念
全部对象最终都会继承自Object.prototype
对象的原型只能是一个Object或者是null,所以我构造一个没有原型的对象,如下
结论
绝大多数对象最终都会继承自Object.prototype,有的对象没有原型
再补充一下
null和undefiend不能调用toString()
因为它们没有原型
call/apply
改变this指向
call
function test(){}
test() //其实执行会默认为test.call()
test()和test.call()没有区别
function Person(name,age){
//this=obj
this.name=name;
this.age=age
}
//没有new,this指向window
var person = new Person('deng',100);
var obj={}
Person.call(obj,'cheng',200);//第二,三...是参数
call()就是改变this的指向的,上面的栗子就是让obj使用Person的方法构造对象,这时person构造函数的this为obj,相当于obj.name=name,obj.age=age;
传参
需要把实参按照形参的个数传进去
用法
当两个对象含有相同的属性时,但是另一个对象的属性比较多(功能涵盖)
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
function Student(name,age,sex,tel,grade){
this.name=name;
this.age=age;
this.sex=sex;
this.tel=tel;
this.grade=grade;
}
var student= new Student('Sunny',123,'male',139,2019);
上面构造对象的方式显得累赘,我们可以用call的方式使用另一个对象的构造方法
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
function Student(name,age,sex,tel,grade){
Person.call(this,name,age,sex);
this.tel=tel;
this.grade=grade;
}
var student= new Student('Sunny',123,'male',139,2019);
结论
对象名.call(对象名,参数...)可以实现借用别的对象的属性来构造本对象的属性
apply
使用方式:对象名.(this,[参数])
区别
传参列表不同,call需要把实参按照形参的个数传进去,apply需要传一个arguments
最后看一张图来缕一缕自己的思路吧
function Add() {}
var add=new Add();