构造函数
什么是构造函数?
我们用一个事例来说说,如何用构造函数来创建一个对象。分两步:
1、定义一个构造函数(可包含属性和方法)
2、通过new
调用构造函数创建实例
// 定义构造函数
function Person(name){
// 属性
this.name = name;
// 方法
this.getAge = function() {
return 18;
}
}
// 通过 new 调用函数创建实例对象
var P = new Person('谷底飞龙')
console.log(`my name is ${P.name}, my age is ${P.getAge()}`)
通过上面的例子,我们会发现通过new创建的对象可以访问构造函数内部this指向的属性和方法。
new 调用构造函数发生了什么?
调用new Person('谷底飞龙')
的执行过程,经历4个阶段:
var obj ={};
obj.__proto__ = Person.prototype;
Person.call(obj);
return obj;
- 1、创建新的空对象
obj
- 2、将构造函数Person的原型
prototype
赋值给创建的新对象obj的__proto__
,这是最关键的一步,具体细节将在下文描述。 - 3、通过
call
将新对象obj
与构造函数内部的this进行硬绑定(可参考call和apply的原理及区别),因此,新对象obj能访问到函数内部this的属性和方法。 - 4、返回一个对象(默认返回this)
obj;
Symbol 是不是构造函数?
判断是否是构造函数,直接用new来调用执行试试
new Symbol();
执行后,会发现报错Uncaught TypeError: Symbol is not a constructor
。因此,Symbol
不支持使用new调用,所以不是构造函数,属于基本数据类型。
我们直接使用Symbol()
试试,会打印出Symbol()
var sym = Symbol()
console.log(sym)
因此,Symbol虽然是基本数据类型,但是可以通过Symbol()
来生成实例,且我们会发现实例sym
有constructor
属性值,值为ƒ Symbol() { [native code] }
。这里的constructor
属性值哪里来的呢?其实是Symbol原型Symbol.prototype.constructor
上的,默认是Symbol()
函数。
constructor 属性是否只读?
- 1、引用类型的
constructor
属性是可以修改的
案例正在补充中
- 2、基本数据类型
number
、string
、bool
、Symbol
等有constructor属性的,constructor
属性是只读的。我们来运行代码试试:
let a = 1;
console.log(a.constructor);//ƒ Number() { [native code] }
console.log("谷底飞龙".constructor);//ƒ String() { [native code] }
console.log(true.constructor);//ƒ Boolean() { [native code] }
console.log(Symbol().constructor);//ƒ Symbol() { [native code] }
- 3、基本数据类型
null
、undefined
是没有constructor属性的
console.log(null.constructor);//Uncaught TypeError: Cannot read property 'constructor' of null
console.log(undefined.constructor);//Uncaught TypeError: Cannot read property 'constructor' of undefined
原型
什么是原型 prototype?
因此,给已存在的构造器添加属性和方法,需要通过原型来添加。我们先来试试不通过原型来添加属性,比如给下面的构造器 Person
增加新属性 weight
// 定义构造函数
function Person(name){
// 属性
this.name = name;
// 方法
this.getAge = function() {
return 18;
}
}
// 通过new调用函数创建实例对象
var P = new Person('谷底飞龙');
// 给 Person 增加新属性 weight
Person.weight = 65;
console.log(`my weight is ${P.weight}`)
添加属性失败,会打印出my weight is undefined
。如果要给构造器添加属性和方法,可以通过构造函数的原型 prototype
来添加,如下:
// 定义构造函数
function Person(name){
// 属性
this.name = name;
// 方法
this.getAge = function() {
return 18;
}
}
// 通过 new 调用函数创建实例对象
var P = new Person('谷底飞龙');
// 给 Person 增加新属性 weight 和方法 getHeight
Person.prototype.weight = 65;
Person.prototype.getHeight = function(){
return 165;
};
console.log(`my weight is ${P.weight}, my height is ${P.getHeight()}`)
通过构造函数的原型可成功添加属性weight
和方法 getHeight()
, 打印出my weight is 65, my height is 165
prototype、[[Prototype]]和__proto__的区别?
从前面讲到的new调用构造函数的执行过程的第二步:obj.__proto__ = Person.prototype
,我们可以看出
- 1、原型
prototype
是构造函数的属性,__proto__
是 new 生成的对象的属性 - 2、构造函数的原型
prototype
和其对象的__proto__
是赋值关系,因此指向同一个对象。如下面的例子,会打印出true
// 定义构造函数
function Person(name){
// 属性
this.name = name;
// 方法
this.getAge = function() {
return 18;
}
}
// 通过new调用函数创建实例对象
var P = new Person('谷底飞龙')
// 对象的 __proto__ 和构造函数的原型 prototype 指向同一个对象
console.log(P.__proto__ === Person.prototype)
-
3、
[[Prototype]]
是对象的内部属性,指向它的构造函数的原型prototype
,外部无法直接访问,可以通过__proto__
来访问内部属性[[Prototype]]
,值得注意的是,__proto__
属性并非ECMAScript标准推荐使用的属性,并且是作为弃用的属性。 -
4、内部属性
[[Prototype]]
无法直接在代码中使用,要用函数Object.getPrototypeOf
来获取它的值,用函数Object.setPrototypeOf
来改变它的值。想了解的的更详细,可以看看这篇文章 Javascript:内部属性[[Prototype]]
原型链
什么是原型链?
我们来看个例子
function Person(name){
this.name = name;
}
var P = new Person('谷底飞龙')
// 打印对象
console.log(P)
打印出对象P
,如下:
我们可以看到,对象P
有一个原型对象__proto__
,P的原型对象__proto__
有两个属性constructor
和自己的原型对象__proto__
,依次下去,最终指向null
。这个案例中的原型链关系:P.__proto__ => P.__proto__.__proto__ => null
原型链继承
instanceof 原理及实现
运作机制及属性遮蔽
参考文档
- 深入理解js构造函数
- 重新认识构造函数、原型和原型链
- JavaScript prototype(原型对象)
- 一篇文章看懂_proto_和prototype的关系及区别
- Javascript:内部属性[[Prototype]]
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!