面向对象
构造函数
构造函数与普通函数是一样的,只不过通常内部使用 this 关键字代表索要生成的对象,在生成对象时需要使用 new 关键字进行创建以构造函数为类模板的对象
为了区别构造函数还是普通函数,构造函数首字母通常大写
New 命令
创建一个空对象;让构造函数中的 this 指向这个创建的对象;将空对象的原型指向构造函数的原型对象;执行构造函数,并返回 this 对象
函数内部又 return 语句,并且返回的是个对象,则会返回 return 语句后指定的对象,否则会忽略 return 语句直接返回 this 对象,返回 return 后的内容
未使用 new 关键字则会当成普通函数对待,this可能会指向全局对象导致污染全局变量的问题,所以为了使构造函数必须使用 new 关键字来进行调用,可以使用如下几种方式
- 使用严格模式,函数首行加
use strict,实质是将 this 为全局对象改为了undefined,所以未使用new关键字会报错 this instanceof 构造函数名,判断this当前对象是否是当前构造函数的实例,当未使用new关键字this就会指向全局对象所以返回falsenew.target属性,若未使用new关键字调用则值是undefined,若使用new关键字则该属性指向当前构造函数
This 指向
this 所指代的是运行时环境,即调用者,当运行时环境发生改变时 this 的指向也会发生改变,所以要尽可能的避免出现 this 指向全局对象的情况
尤其要注意的是:
若有多个对象嵌套时,this 是不会越级的,也就是说内层对象中的 this 指向内层对象,不会指向外层对象,比如
outerObj.innerObj.fun()的方法指向是outerObj.innerObj对象而不是outerObj对象,换言之:总是指向该方法点前面的对象,点前面没有东西则指向全局对象
| 环境 | this 指向 |
|---|---|
| 全局环境中 | 全局对象 |
| 对象环境中 | 该对象 |
| 普通函数中 | 全局对象,实质是由全局对象调用的 |
| 立即执行函数中 | 全局对象,也属于普通函数的一种 |
| 回调函数中 | 全局对象,也属于普通函数的一种 |
| 构造函数中 | 创建的实例对象 |
| 对象方法中 | 调用该方法的对象 |
| 对象的原型中 | 调用该方法的对象,也属于对象方法中 |
| 构造函数的原型对象中 | 构造函数的原型对象,也属于对象方法中 |
| 事件绑定的方法中 | 绑定事件的对象,也属于对象方法中 |
多数情况 this 指向不明确都是由于函数执行的环境,所以为了避免这种情况出现,就出现了一种固定或切换 this 指向的机制
第一个参数都是 this 的指向对象,若是空参、null、undefined 则默认传入全局对象,若是原始类型则会自动转化成对于的包装对象
| 函数名 | 描述 |
|---|---|
Function.prototype.call(objectThis,...ages) |
以当前指向对象为环境执行函数,参数逐个传入 |
Function.prototype.apply(objectThis,array) |
以当前指向对象为环境执行函数,参数以数组形式传入 |
Function.prototype.bind(objectThis,...ages) |
返回一个以指向对象为环境的函数拷贝,参数逐个传入 |
原型对象
由同一个构造函数生成的实例对象,属性是无法进行共享的,对于相同的属性会在内存中开辟两块内存不同内容相同的空间,从而导致系统资源的浪费,所以 js 引入了 prototype 原型对象,原型对象上的所有属性和方法都能被实例对象共享
每个函数都有一个 prototype 属性指向原型对象,该属性对普通函数来说是毫无意义的,对于构造函数来说该属性会自动成为实例对象的原型,即对象原型指向原型对象
在 prototype 上的属性无法使用构造函数名直接调用,而构造函数的实例对象可以直接调用,所以称为实例属性;在构造函数上直接添加的属性可以通过构造函数名直接调用,所以称为静态属性
Constructor 属性
每个 prototype 对象都有一个 constructor 属性指向构造函数,所以修改原型对象时最好同时修改好原型对象的 constructor 属性,更好的方式是在原本原型对象基础上增加方法,而不是直接更改为另一个对象
原型链
js 中每个对象默认都有自己的原型指向自己的原型对象,原型对象也是对象也有自己的原型,所以就会形成一个原型链,所有对象最终会指向 Object.prototype,而 Object.prototype 对象的原型是 null 原型链到此结束
读取对象的某个属性时,先寻找自身属性,若没有找到会到原型中找,再找不到就到原型的上级原型找,直到顶层 Object.prototype 为止;若本身和原型中都有该属性则会就近原则选择属性,若想不就近原则调用的话就必须显示的加上是那一层原型的属性
function F(){}
F.prototype.toString = function(){
return "toString";
}
var obj = new F();
console.log(Object.prototype.toString()); //[object Object]
console.log(F.toString()); //toString
获取对象原型的三种方式:
obj.__proto__:只有浏览器才需要部署,其他环境中不需要部署,node 环境中也有,即将废弃,不建议使用obj.constructor.prototype:由于原型链相当于是obj.__proto__.constructor.prototype的简写,由上图可知,由于就近原则,无法直接获得上级原型,一直来回指,除非借助__proto__或Object.getPrototype(obj)Object.getPrototypeOf(obj):最可靠的方式
/*获取整个原型链*/
function F(){}
var obj = new F();
/*第一层原型,是obj对象的原型,为构造函数的原型对象*/
console.log(obj.__proto__ === F.prototype); //true
console.log(obj.constructor.prototype === F.prototype); //true //等同于 console.log(obj.__proto__.constructor.prototype === F.prototype); //true
console.log(Object.getPrototypeOf(obj) === F.prototype); //true
/*第二层原型,是构造函数的原型对象的原型,为Object的原型对象*/
console.log(obj.__proto__.__proto__ === Object.prototype); //true
console.log(Object.getPrototypeOf(obj.constructor.prototype) === Object.prototype); //true
//等同于
console.log(F.prototype.__proto__ === Object.prototype); //true
console.log(Object.getPrototypeOf(F.prototype) === Object.prototype); //true
/*第三层原型是Object原型对象的原型,为null*/
console.log(obj.__proto__.__proto__.__proto__ === null); //true
console.log(Object.getPrototypeOf(Object.getPrototypeOf(obj.constructor.prototype)) === null); //true
//等同于
console.log(Object.prototype.__proto__ === null); //true
console.log(Object.getPrototypeOf(Object.prototype) === null); //true
继承
function Super(){} //父类
Super.prototype.fun = function(){}; //父类的实例方法
function Sub() { //子类
Super.call(this); //调用父类构造函数,相当于将父类的构造函数拷贝过来了
}
Sub.prototype = Object.create(Super.prototype); //修改子类原型对象指向父类对象
//不能使用等号直接赋值,否则修改构造属性时会将父类的一同修改掉
// Sub.prototype = new Super(); //这种方式会带有父类的静态属性
//等同于 Object.setPrototypeOf(Sub,Object.create(Super.prototype));
Sub.prototype.constructor = Sub; //修改构造属性子类构造属性指向子类构造函数
Sub.prototype.fun = function(){}; //子类实例方法覆盖了父类的方法

Comments NOTHING