复杂数据类型的引用
function person(){...}
var x = person; // 不会创建 person 的副本,是引用
//使用new才会创建副本
如果修改 x ,person 的属性也会改变:
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}
var x = person;
x.age = 10; // x.age 和 person.age 都会改变
创建对象三种方法
- var a={ }
- var a=new Object( )
- 构造函数
原型对象 prototype
what
所有的 JavaScript 对象都会有一个 prototype 属性,其指向 prototype 对象(原型对象) ,
所有对象都会从原型对象中继承属性和方法
prototype作用
prototype可以用来存放共享方法,因为如果方法写在构造器中,每次创建实例都会为方法开辟空间。
属性添加位置
function Person(first, last) {
this.firstName = first;//实例成员
this.lastName = last;//实例成员
}
var p=new Person();
Person.nationality = "English";//静态成员
console.log(Person.nationality)//English,静态成员通过函数名访问
console.log(p.nationality)//undifined,静态成员无法通过实例访问
所以要添加一个新的属性需要在构造器函数中添加:
function Person(first, last) {
this.firstName = first;
this.lastName = last;
this.nationality = "English";
}
prototype,__ proto __
__ proto __,构造函数prototype属性都指向prototype原型对象,
(prototype)原型对象中有constructor,__ proto__,和其他手动添加的属性和方法,
注意区分:
实例中包含 在构造器中添加的属性和方法,__ proto__ ;//实例. __ proto__
而prototype是构造函数的属性,指向prototype对象,实例没有prototype;//构造函数.prototype
注: 在chrome,Firefox,safari中__ proto__就是[[ Prototyoe ]],其他环境下没有[[ Prototyoe ]]的标准方式
new的过程
1.在内存中创建空对象
2.让this指向空对象
3.执行构造函数的代码,给空对象加属性和方法
4.返回这个对象(所以构造函数里不需要 return ;如果构造函数写了return,按写的return执行)
var obj = {};//创建一个空对象(即{});
obj.__proto__= ClassA.prototype;//为创建的对象添加属性__proto__,将该属性指向构造函数的prototype对象;
const result= ClassA.call(obj); //新对象调用函数,函数中的this被指向新实例对象;
//最后将初始化完毕的新对象地址return,保存到等号左边的变量中
if(result&&typeof result==='object'||typeof result==='function'){
return result;
}//考虑到构造函数ClassA中是否有return,若有对象或函数return,就return result
return obj;
new 和不 new的区别:
- 如果 new 了函数内的 this 会指向当前这个 person 并且就算函数内部不 return 也会返回一个对象。
- 如果不 new 的话函数内的 this 指向的是 window。
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
return [this.firstname,this.lastname,this.age,this.eyecolor,this] //
}
var myFather=new person("John","Doe",50,"blue");
var myMother=person("Sally","Rally",48,"green");
console.log(myFather) // this 输出一个 person 对象
console.log(myMother) // this 输出 window 对象
prototype.constructor指向原来的构造函数
// 构造器内定义属性
function Fun(a, b) {
this.a = a;
this.b = b;
}
// 原型属性定义方法
Fun.prototype.c = function() {
return this.a + this.b;
}
var a=new Fun(1,2);
//prototype.constructor指向原来的构造函数,如:
console.log(Fun.prototype.constructor)//function Fun(a, b) {this.a = a;this.b = b;}
慎用字面量方式来定义 prototype 属性和方法
// 注意,千万不要使用字面量方式来定义属性和方法,否则原有属性和方法会被重写:
function Fn() {};
// 定义属性
Fn.prototype.a = 1;
Fn.prototype.b = 2;
// 字面量定义方法,原型被重写,原有属性和方法被更新
Fn.prototype = {
//使用 constructor:Fn; 手动补救,可以指回原来的构造函数
c : function() {
return this.a + this.b;
}
}
var foo = new Fn();
foo.c(); // NaN
foo.a; // undefined
foo.b; // undefined
class类
class本质是构造函数的语法糖
class demoName{
constructor(){
a=10,
name="hai"
},
demoMethods1(){
this.demoMethod2();//this不能忘了加
},
demoMethods2(){
}
}
继承
1.原型链继承
个人理解:把父类的东西备份一个到子类中,需要的时候就沿着链去备份中找,而且实例共用一个备份
function Animal(){
this.colors=["black","white"];
}
Animal.prototype.getColor=function(){
return this.colors;
}
function Dog(){};
Dog.prototype=new Animal();//dog1和dog2引用了同一个原型对象
let dog1=new Dog();
dog1.colors.push("brown");//将Dog.prototype改变,但不会改变Animal的colors
let an=new Animal();
console.log(an.colors);//["black", "white"]
let dog2=new Dog();
console.log(dog2.colors)// ["black", "white", "brown"]
缺点:
- 原型中的引用将被所有实例共享,实例更改会相互影响,如上colors的更改
- 子类在实例化时不能给父类构造函数传入参数,如上:new Dog( )不能给Animal( )传参
2.盗用构造函数实现继承
理解:相当于把父类构造器的内容搬到子类构造器运行
如:Animal.call(this,name)
function Animal(){
this.name=name;
this.getName=function(){//前面说过,方法最好不要写在constructor中,下个方法会优化
return this.name;
}
}
Animal.prototype.getName=function(){
return "hello";
}
function Dog(name){ //可以传入参数了
Animal.call(this,name);//继承父类constructor中的内容
}
/* 实例中包含 constructor中添加的属性和方法,__proto__ */
Dog.prototype=new Animal(); //继承父类 实例的__proto__指向的 prototype的内容
//以下是一些尝试,看看构造函数和实例里面都有什么
let r=new Dog("r");
console.log(Dog.prototype.__proto__.getName);
//Dog.prototype是实例,别用prototype,上面没有
console.log(Dog.prototype.__proto__.getName===r.getName);//false
console.log(r.__proto__.__proto__.getName);//即Dog.prototype.__proto__.getName
3.盗用构造函数结合原型链实现继承
function Animal(){
this.name=name;
}
Animal.prototype.getName=function(){ //实例共用方法,不用反复创建
return this.name;
}
function Dog(name){
Animal.call(this,name);//继承父类constructor中的内容
}
Dog.prototype=new Animal(); //继承父类prototype的内容
Dog.prototype.constructor=Dog;//补上constructor;
4.寄生组合式继承
//原来
Dog.prototype=new Animal();
Dog.prototype.constructor=Dog;
//寄生组合式
Dog.prototype=Object.create(Animal.prototype);
//过滤了Animal中的constructor部分,不用重复继承多余的constructor
Dog.prototype.constructor=Dog;
Object.create()//已简化
function inheeritPrototype(son,father){
function obj(father_proto){
function f(){};
f.prototype=father_proto;
return new f();
};
let prototypeS=new obj(father.prototype);
prototypeS.constructor=son;
//prtotypeS相当于father,但使用不同空间,避免了相互影响
son.prtotype=prototypeS;
}
inheeritPrototype(Dog,Animal);