构造函数,就是专门用来生成实例对象的函数。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。
function Person(name){
this.name = name;
}
为了与普通函数区别,构造函数名字的第一个字母通常大写。
构造函数的特点有两个:
new 命令的作用,就是执行构造函数,返回一个实例对象。
let a = new Person('dora');
a.name // dora
new 命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号,但为了表明是函数调用,推荐使用括号表示更明确的语义。
使用 new 命令时,它后面的函数依次执行下面的步骤:
如果构造函数内部有 return 语句,而且后面跟着一个对象,则 new 命令会返回 return 语句指定的对象;否则,就会不管 return 语句,返回 this 对象,且不会执行 return 后面的语句。
function Person(name) {
this.name = name;
if (name == undefined) {
return {};
}else if(typeof name != 'string'){
return '姓名有误';
}
console.log(111);
}
new Person(); // {}
new Person(123); // {name: 123}
new Person('dora');
// {name:'dora'}
// 111
如果当前函数是 new 命令调用的,在函数内部的 new.target 属性指向当前函数,否则为 undefined。
function F() {
console.log(new.target === F);
}
F() // false
new F() // true
使用这个属性,可以判断函数调用时,是否使用了 new 命令。
function F() {
if (!new.target) {
throw new Error('请使用 new 命令调用!');
}
}
F() // false
new F() // true
如果不使用 new 命令执行构造函数就会引发一些意想不到的结果,所以为了保证构造函数必须与 new 命令一起使用,除了 new.target 之外也可以有以下两个解决办法。
在构造函数内部第一行加上 use strict。这样的话,一旦忘了使用 new 命令,直接调用构造函数就会报错。
function Person(name, age){
'use strict';
this.name = name;
this.age = age;
}
Person()
// TypeError: Cannot set property 'name' of undefined
报错原因是因为不加 new 调用构造函数时,this 指向全局对象,而严格模式下,this 不能指向全局对象,默认等于 undefined,给 undefined 添加属性肯定会报错。
function Person(name, age) {
if (!(this instanceof Person)) {
return new Person(name, age);
}
this.name = name;
this.age = age;
}
Person('dora', 18).name // dora
(new Person('dora', 18)).age // 18
在构造函数内部判断 this 是否是构造函数的实例,如果不是,则直接返回一个实例对象。
任何函数都有一个 prototype 属性,这个属性称为函数的“原型”,属性值是一个对象。只有函数有原型属性。
function f(){}
typeof f.prototype // "object"
对于普通函数来说,原型没有什么用。但对于构造函数来说,通过 new 生成实例的时候,该属性会自动成为实例对象的原型对象。
用来定义所有实例对象共用的属性和方法。
如果将对象的方法写入构造函数中,则 new 多少个实例,方法将会被复制多少次,虽然复制出来的函数是一样的,但分别指向不同的引用地址,不利于函数的复用。
function Person(){
this.name = function(){
console.log('dora');
}
}
let p1 = new Person();
let p2 = new Person();
p1.name === p2.name // false
因此,将所有的属性都定义在构造函数里,所有的方法都定义在构造函数的原型中。这样,实例的方法都指向同一个引用地址,内存消耗小很多。
函数原型 prototype 对象的属性,指向这个原型所在的构造函数,可以被所有实例对象继承,指向构造自己的构造函数。
Person.prototype.constructor.name // "Person"
p1.constructor.name // "Person"
函数的 name 属性返回函数名。
Person.prototype.sayHi = function(){
console.log('Hi,I am Dora');
}
Person.prototype = {
sayHi: function(){
console.log('Hi,I am Dora');
}
}
这样用对象字面量直接覆盖,会让 constructor 与构造函数失联,可以手动补上这个属性。
Person.prototype = {
constructor: Person,
sayHi: function(){
console.log('Hi,I am Dora');
}
}
定义构造函数原型中的方法时尽量不要相互嵌套,各方法最好相互独立。
任何一个对象都有 __proto__ 属性,这个属性称为对象的“原型对象”,一个对象的原型对象就是它的构造函数的 prototype。
function Person(){}
let p1 = new Person();
p1.__proto__ === Person.prototype // true
__proto__ 并不是语言本身的属性,这是各大浏览器厂商添加的私有属性,虽然目前很多浏览器都可以识别这个属性,但依旧不建议在生产环境下使用,避免对环境产生依赖。
生产环境下,我们可以使用 Object.getPrototypeOf(obj) 方法来获取参数对象的原型。
Object.getPrototypeOf(p1) === Person.prototype // true
当访问对象的属性时,如果这个对象没有这个属性,系统就会查找这个对象的 __proto__ 原型对象,原型对象也是个对象,也有自己的 __proto__ 原型对象,然后就会按照这个原型链依次往上查找,直到原型链的终点 Object.prototype。
Object() 是系统内置的构造函数,用来创建对象的, Object.prototype 是所有对象的原型链顶端,而Object.prototype的原型对象是 null。
Object.getPrototypeOf(Object.prototype) // null
如果对象自身和它的原型都定义了一个同名属性,那么优先读取对象自身的属性。
继承的核心是 子类构造函数的原型是父类构造函数的一个实例对象。
首先继承父类的属性
// 1. 父类构造函数
function Super(data){
this.data = data;
};
Super.prototype.funName = function(){};
// 2. 子类构造函数
function Sub(){
// 用来继承父类的参数和属性
Super.apply(this, arguments);
}
其次继承父类的方法
整体继承
Sub.prototype = Object.create(Super.prototype);
// or
Sub.prototype = new Super();
单个方法的继承
Sub.prototype.funName = function(){
Super.prototype.funName.call(this);
// some other code
}
最后需要改变 constructor 指向
此时子类实例的 constructor 指向父类构造函数 Super,需手动改变。
Sub.prototype.constructor = Sub;
ES5 没有多重继承功能,即不允许一个对象同时继承多个对象,但可通过变通方法实现这个功能。
function S(){
M1.call(this);
M2.call(this);
};
S.prototype = Object.create(M1.prototype); // 继承 M1
Object.assign(S.prototype,M2.prototype); // 继承链上加入 M2
S.prototype.constructor = S; // 指定构造函数。
instanceof 运算符返回一个布尔值,表示对象是否为某个构造函数的实例。继承的子类实例也是父类的实例,因此继承的也为 true。
let d = new Date();
d instanceof Date // true
d instanceof Object // true
实例对象可继承 isProtorypeOf()方法,用来判断该对象是否为参数对象的原型对象。
只要实例对象处在参数对象的原型链上,isPrototypeOf() 方法都返回 true。
let o1 = {};
let o2 = Object.create(o1);
let o3 = Object.create(o2);
o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true
o2.isPrototypeOf(o2) // false
原型继承缺点:1、不能由子对象像父对象传递参数,2、对于引用型的属性修改之后会印象其他的实例对象;构造函数继承缺点:1、不能继承父对象原型上的方法 2、每次实例化对象会重新构建函数,浪费内存。
学过java的同学应该都知道,继承是java的重要特点之一,许多面向对象的语言都支持两种继承方式:接口继承和实现继承,接口继承只继承方法签名,而实现继承则继承实际的方法,在js中,由于函数没有签名,因此支持实现继承,而实现继承主要是依靠原型链来实现的,那么,什么是原型链呢?
在面向对象语言都会存在继承的概念,在面向对象语言中,继承的特点:继承了父类的属性和方法。那么我们现在主要研究css,css就是在设置属性的。不会牵扯到方法的层面。
1. 原型链继承;2,构造函数继承(对象冒充继承);3,组合继承(原型链继承+构造函数继承);4,原型式继承;5. 寄生组合式继承,为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术
prototype是构造函数的一个属性,它决定了在构造出来的对象上__proto__属性将是什么样的。如上图所示,理解JavaScript中的继承的关键是要理解母鸡如何产蛋的过程。
JavaScript常用继承方式主要分为(7种):原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承以及继承多个对象。原型链继承(核心:将父类的实例作为子类的原型
继承到底是什么?继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”。继承可以使得子类具有父类别的各种属性和方法
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。如果让原型对象指向另一个类型的实例.....有趣的事情便发生了.
JavaScript对象继承的方法有很多,这里总结一下几种比较常用的方法。使用call/apply和Object.create()第一种方法使用call或apply方法,改变了 this 的指向而实现继承,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行
要搞懂JS继承,我们首先要理解原型链:每一个实例对象都有一个__proto__属性(隐式原型),在js内部用来查找原型链;每一个构造函数都有prototype属性(显示原型),用来显示修改对象的原型
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!