05-面向对象

nobility 发布于 2022-10-04 01-ES5 412 次阅读


面向对象

构造函数

构造函数与普通函数是一样的,只不过通常内部使用 this 关键字代表索要生成的对象,在生成对象时需要使用 new 关键字进行创建以构造函数为类模板的对象

为了区别构造函数还是普通函数,构造函数首字母通常大写

New 命令

创建一个空对象;让构造函数中的 this 指向这个创建的对象;将空对象的原型指向构造函数的原型对象;执行构造函数,并返回 this 对象

函数内部又 return 语句,并且返回的是个对象,则会返回 return 语句后指定的对象,否则会忽略 return 语句直接返回 this 对象,返回 return 后的内容

未使用 new 关键字则会当成普通函数对待,this可能会指向全局对象导致污染全局变量的问题,所以为了使构造函数必须使用 new 关键字来进行调用,可以使用如下几种方式

  1. 使用严格模式,函数首行加 use strict,实质是将 this 为全局对象改为了 undefined,所以未使用 new 关键字会报错
  2. this instanceof 构造函数名,判断 this 当前对象是否是当前构造函数的实例,当未使用 new 关键字 this 就会指向全局对象所以返回 false
  3. new.target 属性,若未使用 new 关键字调用则值是 undefined,若使用 new 关键字则该属性指向当前构造函数

This 指向

this 所指代的是运行时环境,即调用者,当运行时环境发生改变时 this 的指向也会发生改变,所以要尽可能的避免出现 this 指向全局对象的情况

尤其要注意的是:

若有多个对象嵌套时,this 是不会越级的,也就是说内层对象中的 this 指向内层对象,不会指向外层对象,比如 outerObj.innerObj.fun() 的方法指向是 outerObj.innerObj 对象而不是 outerObj 对象,换言之:总是指向该方法点前面的对象,点前面没有东西则指向全局对象

环境 this 指向
全局环境中 全局对象
对象环境中 该对象
普通函数中 全局对象,实质是由全局对象调用的
立即执行函数中 全局对象,也属于普通函数的一种
回调函数中 全局对象,也属于普通函数的一种
构造函数中 创建的实例对象
对象方法中 调用该方法的对象
对象的原型中 调用该方法的对象,也属于对象方法中
构造函数的原型对象中 构造函数的原型对象,也属于对象方法中
事件绑定的方法中 绑定事件的对象,也属于对象方法中

多数情况 this 指向不明确都是由于函数执行的环境,所以为了避免这种情况出现,就出现了一种固定或切换 this 指向的机制

第一个参数都是 this 的指向对象,若是空参、nullundefined 则默认传入全局对象,若是原始类型则会自动转化成对于的包装对象

函数名 描述
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

获取对象原型的三种方式:

  1. obj.__proto__:只有浏览器才需要部署,其他环境中不需要部署,node 环境中也有,即将废弃,不建议使用
  2. obj.constructor.prototype:由于原型链相当于是 obj.__proto__.constructor.prototype 的简写,由上图可知,由于就近原则,无法直接获得上级原型,一直来回指,除非借助 __proto__Object.getPrototype(obj)
  3. 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(){};	//子类实例方法覆盖了父类的方法
加油啊!即便没有转生到异世界,也要拿出真本事!!!\(`Δ’)/
最后更新于 2022-10-04