更多
更多
文章目录
  1. 创建对象的几种方式
  2. __proto__和prototype的区别
  3. 继承

Javascript之构造函数

当我们重复操作一些属性方法时,就可以想到将属性方法进行一定得封装方便使用,这个时候我们就面临两种选择

  • 普通函数
  • 构造函数

简单的区分一下普通函数和构造函数:

当我们直接调用一个方法的时候,他就叫普通函数,此时用全局对象的window作为上下文,

用new操作符调用的时候他叫构造函数,此时用新生对象作为上下文,

普通函数比较理解,今天重点理解一下构造函数。

什么时候使用构造函数?

当对象有很多实例,可以被标志为特定的类型,可以通过继承扩展代码时,我们就快可以使用构造函数。

创建对象的几种方式

  • 使用new关键字创建
1
2
3
4
var cq=new Object();
cq.name='chenqi';
cq.bar='html';
cq.hello=function(){……}
  • 使用字面量创建

就是平常建立普通对象的一种方式

1
2
3
4
5
var cq={
name:'chenqi',
bar:'html',
hello:function(){……}
}
  • 工厂模式

当我们需要写多个相似的对象时,就可以用工厂函数(专门造对象的一个函数)来进行创建

1
2
3
4
5
6
7
function createCQ(name,bar){
var o=new Object();
o.name=name;
o.bar=bar;
return o;
}
var cq1=createCQ('bingbing','js');
  • 构造函数

本质上是一个工厂函数,像这种用new来生成一个对象我们称它为构造器(constructor),

每个实例都有一个constructor属性,默认调用prototype对象的constructor属性,object.constructor可以查看对象的构造函数,

1
console.log(cq1.constructor==CreateCQ.prototype.constructor);//true

一般情况下构造函数的第一个字母需要大小,来和普通函数作为区分

生成对象的过程叫做实例化

1
2
3
4
5
6
7
8
function CQ(name,bar){
this.name=name;//this指即将生成的对象
this.bar=bar;
this.eat=function(){
console.log('miamiamia……')
}
}
var cq1=new CQ('chenchen','fff')
  • .create()方法创建对象
1
2
3
var a=Object.create(null);
console.log(a);
//此方法创建出来的对象是最干净的对象,没有prototype,constractor等属性

__proto__prototype的区别

__prototype__(隐式原型)与prototype(显式原型)

显式原型的作用:用来实现基于原型的继承与属性的共享。

隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次查找。

几乎任何一个对象都有prototype属性,这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的方法和属性(我们把这个对象叫做原型对象

这意味着我们可以把那些不变的属性和方法,直接定义在prototype上,

例如构造函数Foo(),这个构造函数的属性Foo.prototype指向了原型对象,它保存着实例共享的方法

通过原型的方式来指定一个方法,如果中途改变了这个方法,所有引用它的方法都会跟着做出相应的变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function User(name,age){//原型对象
this.name=name;
this.age=age;
}
User.prototype.greet=function(){
console.log(this.name+"我今年"+this.age+'岁了');
}
var chichi=new User('chichi',19);
var chenchen=new User('chenchen',18);

console.log(User.prototype.isPrototypeOf(whh));
//判断源性对象和实例之间的关系
console.log(whh.hasOwnProperty('name'));
//判断whh对象是本地属性还是继承自prototype对象的属性
console.log('age' in whh)
//in运算符判断实例是否含有某个属性不管是不是本地属性

总结

可理解为__proto__ = constructor.prototype

__proto__是一些浏览器提供的一个查看prototype的接口,也就是说prototype才是标准的原型、可用在代码中,而__proto__用于调试。

在chrome调试中,函数的原型可以用prototype查看,而__proto__可以看所有对象的原型。

1.每个对象有属性__proto__,指向该对象的构造函数的原型对象
2.只有函数方法才有属性prototype,prototype指向该方法的原型对象

对象在调用一个方法时会首先在自身里寻找是否有该方法,若没有,则通过内部的__proto__属性去自己的构造函数原型上去寻找,依次层层递进,直到Object.prototype.__proto__=null为止,这样往上追溯的整个过程,即原型链

继承

  • 显性属性用call/apply改变作用于的方法更换作用域范围
  • 隐性属性通过重写prototype对象的方式进行继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Animal(color,age){
this.color=color;
this.age=age;
}
Animal.prototype={
sleep:function(){
console.log('我要睡觉');
}
}
//不变的属性方法写在prototype对象里

function Person(color,age){
Animal.call(this,color,age);
}
//显性属性用call/apply改变作用于的方法更换作用域范围
Person.prototype=Object.create(Animal.prototype);
Person.prototype.constructor=Person;
//隐性属性通过重写prototype对象的方式进行继承,
//因为重写了prototype覆盖了原先的prototype属性,当前没有指定constuctor函数就会一级一级的向上查找到animal的constructor会造成原型构造函数混乱的情况
//所以新对象中要重新指定当前构造函数的constructor
Person.prototype.eat=function(){
console.log('我要吃饭');
}

var animal=new Animal('black',2);
console.log(animal);

var person =new Person('white',3);
console.log(person);
// console.log(person.constructor==Person.prototype.constructor)
感谢阅读
公众号