在ES6之前,对象不是通过类创建的,而是用构造函数的特殊函数来定义。
创建对象可以通过以下三种方式:
- 对象字面量 var obj = {}
- new Object()
- 自定义构造函数 function Foo(){}
构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到一个函数里面。
构造函数与普通函数的区别
- 构造函数一般首字母大写
- 调用方式不一样,作用也不一样
- 普通函数:直接调用 ();
- 构造函数:需要使用new关键字 new Person();
- 构造函数内部使用this来构造属性和方法
function Person(name,job,age)
{
this.name=name;
this.job=job;
this.age=age;
//一般方法写在原型上,后面讲
this.sayHi=function(){
alert("Hi")
}
}
new 在执行时会做四件事情
- 在内存中创建一个新的对象。
- this 指向这个新对象。
- 给新对象添加属性和方法。
- 返回这个新对象(构造函数不需要return)
返回值不同
普通函数
如果没有设置return,调用时则返回undefined
// 普通函数
function person(){}
var per = person();
console.log(per); //undefined
构造函数
不需要设置return,实例时默认返回该对象
// 构造函数
function Person(){ }
var per = new Person();
console.log(per); //Person {}
静态成员与实例成员
静态成员在构造函数本身添加成员 Star.sex = ""
静态成员只能通过构造函数来访问。
实例成员就是构造函数内部通过this添加的成员
实例成员只能通过实例化对象来访问。
构造函数的问题
构造函数原型 prototype(显式原型)
构造函数通过原型定义的函数是所有实例对象共享的
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象,这个对象的所有方法和属性,都会被构造函数所拥有。
我们可以把相同的方法,直接定义在prototype对象上,这样所有实例对象就可以共享这些方法。
function Star(uname,age){
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function(){
console.log("我会唱歌");
}
var ldh = new Star("ldh",18);
var zxy = new Star("zxy",20);
ldh.sing();
zxy.sing()
一般情况下,公共属性定义到构造函数里面,而公共方法定义在原型对象上。
原型的主要作用是共享方法
对象原型 __proto __ (隐式原型)
创建对象时系统会自动添加一个proto属性指向我们构造函数的原型对象 prototype
- 实例的__ proto__对象原型和构造函数的原型对象prototype是等价的。
- __ proto__对象原型的意义就在于为对象的查找机制提供一个方向。
constructor 构造函数
对象原型(__proto __)和 构造函数原型对象(prototype)里面都有一个cunstructor属性,称为构造函数,因此它指向构造函数本身。
function Star(uname,age){
this.uname = uname;
this.age = age;
}
Star.prototype = {
//如果改变了原来的原型对象,给原型对象赋值的是一个对象,我们需要手动利用constructor
// 指回原来的构造函数。
constructor:Star,
sing: function(){
console.log("我会唱歌");
},
movie: function(){
console.log("我会演电影");
}
}
var ldh = new Star("ldh",18);
var zxy = new Star("zxy",20);
console.log(Star.prototype);
console.log(ldh.__proto__);
console.log(Star.prototype.constructor);
console.log(ldh.__proto__.constructor);
当Star.prototype 以对象的形式添加方法时,会删除constructor属性,所以需要我们手动添加回来
注意:尽量不要给原型对象进行赋值操作,Array.prototype = {},尽量使用Array.prototype.xxx = function(){} 方式,如果不得已记得手动添加constructor属性。
构造函数、实例、原型对象三者之间关系
原型链
//我们Star原型对象里面的__proto__原型指向的是 Object.prototype
console.log(Star.prototype.__proto__ === Object.prototype); //true
console.log(Object.prototype.constructor); //Object
console.log(Object.prototype.__proto__); //null
function Star(uname,age){
this.uname = uname;
this.age = age;
}
Object.prototype.dacen= function(){
console.log("我是object的方法");
}
var ldh = new Star("ldh",18);
ldh.dacen();
我在Object原型对象中定义个dacen方法,ldh实例对象也能访问到。Object是原型链的尽头
查找机制:
ldh实例首先会在自身对象中查找,如果找不到该方法,则会在Stat原型对象中找,如果还找不到,就会通过__ proto__原型,到Object原型对象上找,直到为null。
原型链中Object与Function的关系
函数对象都是有Function函数生成的,而Function自身也是函数,则有Function.__ proto__ === Function.prototype // true (函数是自身的实例)
Object 也是构造函数,所以Object.__ proto__ === Function.prototype //对象是函数的实例
通过隐式原型链,f1.__ proto__ 到 Foo.prototype; 从 Foo.prototype.__ proto__ 到 Object.prototype,所以实例对象都可以访问到Object中的原型对象 ;
通过隐式原型链,f1.__ proto__ 到 Foo.prototype;但 Foo.prototype.__proto __ 不能到Function.prototype(只能是Foo.__proto __===Function.prototype),所以实例对象访问不到函数中的原型对象,只有构造函数才能访问到函数原型对象
函数和对象互为实例的说法是错误的,因为Object.__ proto__ === Function.prototype 但Function.__ proto__ != Object.prototype,因为Function.__ proto__ === Function.prototype,所以我认为只能说,对象是函数的实例,函数的原型对象是对象的实例!
案例
function F(){}
Object.prototype.a= function(){
console.log("a()");
}
Function.prototype.b = function(){
console.log("b()");
}
var f = new F(); //f是一个对象所以找不到Function的原型
// 在原型链上查找
console.log(f.__proto__.__proto__); //Object.prototype
f.a();
// f.b() // undefined
console.log(F.prototype.__proto__); //Object.prototype
F.a()
console.log(F.__proto__ === Function.prototype); //true
F.b();
//我们看不了 Function.prototype 的属性
原型链查找机制
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
- 如果没有就会查找它的原型(也就是__proto __指向的prototype原型对象)。
- 如果还找不到就会查找原型对象(Object的原型对象)。
- 以此类推一直找到Object为止(null)。
- 如果都存在,则就近原则。
原型链继承
// 父
function Supper(){
this.supper = "Supper prototype"
}
Supper.prototype.showSupper = function(){
console.log(this.supper);
}
//子
function Sub(){
this.sub = "Sub prototype"
}
//子类型的原型对象为父类型的一个实例
Sub.prototype = new Supper();
//使constructor 指回Sub
Sub.prototype.constructor = Sub
Sub.prototype.showSub = function(){
console.log(this.sub);
}
// 实例Sub对象
var sub = new Sub();
sub.showSub()
sub.showSupper();
组合继承
- 利用原型链实现对父类型对象的方法继承
- 利用call借用父类型构造函数初始化相同属性
<script>
//父类型
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.setName = function(name){
this.name = name;
}
//子类型
function Student(name,age,price){
//利用call继承属性
Person.call(this,name,age) //相当于 this.Person(name,age)
this,price = price;
}
//利用原型链继承方法
Student.prototype = new Person();
Student.prototype.constructor = Student;
Student.prototype.setPrice = function(price){
this.price = price;
}
var student = new Student("ldh",20,16000)
student.setName("zs")
student.setPrice("123")
console.log(student.name,student.age,student.price); //zs 20 123
</script>