对象与继承

创建一个对象的几种方式
  1. new Object()或者字面量{},或者__proto__;
js 复制代码
let obj = {
  name: 'obj',
  sayName: function () {
    console.log(this.name)
  }
}
// obj ---> Object.prototype ---> null
​// 可以通过 __proto__ 字面量属性将新创建对象的[[Prototype]] 指向另一个对象。
const p = { b: 2, __proto__: o };
// p ---> o ---> Object.prototype ---> null
  • 优点 被所有的现代引擎所支持。将 proto 属性指向非对象的值只会被忽略,而非抛出异常。与 Object.prototype.proto setter 相反,对象字面量初始化器中的 proto 是标准化,被优化的。甚至可以比 Object.create 更高效。在创建对象时声明额外的自有属性比 Object.create 更符合习惯。

  • 缺点 不支持 IE10 及以下的版本。对于不了解其与 Object.prototype.proto 访问器差异的人可能会将两者混淆。

  1. Object.create(proto,propertiesObject);
js 复制代码
// 参数1:原型对象,参数2:可枚举的自有属性
// 参数1不是null或对象,则抛出TypeError异常
// 参数2不是指定结构也会报TypeError错
let obj1 = Object.create(obj, {
  name: {
    value: 'obj1Name', // 默认为undefined
    writable: true, // 默认为false
    configurable: true, // 默认为false
    enumerable: false,
    writable:false
  }
})
obj1.__proto__.__proto__ === obj.__proto__;
obj1.__proto__.__proto__ === Object.prototype;
// 扩展,查看一个对象的原型上是否有某个属性
Object.prototype.hasOwnProperty.call(obj1,'sayName'); //false name为true
// 可模仿new 构造函数
  • 优点 被所有现代引擎所支持。允许在创建时直接设置对象的 [[Prototype]],这允许运行时进一步优化对象。还允许使用 Object.create(null) 创建没有原型的对象。
  • 缺点 不支持 IE8 及以下版本。但是,由于微软已经停止了对运行 IE8 及以下版本的系统的扩展支持,这对大多数应用程序而言应该不是问题。此外,如果使用了第二个参数,慢对象的初始化可能会成为性能瓶颈,因为每个对象描述符属性都有自己单独的描述符对象。当处理上万个对象描述符时,这种延时可能会成为一个严重的问题。
  1. new 构造函数创建;
js 复制代码
function Person(name) {
  // 判断是否用new 创建对象
  if (new.target === void 0) {
    return {}
  }
  this.name = name;
  this.sayName = function () {
    console.log(this.name)
  }
}

const xiaoming = new Person('xiaoming');
  • 优点 所有引擎都支持------一直到 IE 5.5。此外,其速度很快、非常标准,且极易被 JIT 优化。
  • 缺点

1.要使用这个方法,必须初始化该函数。在初始化过程中,构造函数可能会存储每一个对象都必须生成的唯一信息。这些唯一信息只会生成一次,可能会导致问题。

2.构造函数的初始化过程可能会将不需要的方法放到对象上。

  1. 使用类class
js 复制代码
class Obj {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
const obj = new Obj(2, 3);
// obj ---> Obj.prototype ---> Object.prototype ---> null
  • 优点 被所有现代引擎所支持。非常高的可读性和可维护性。私有属性是原型继承中没有简单替代方案的特性。

  • 缺点 类,尤其是带有私有属性的类,比传统的类的性能要差(尽管引擎实现者正在努力改进这一点)。不支持旧环境,通常需要转译器才能在生产中使用类。

    new 都干了什么

    1. 创建了一个空的对象{}
    2. 为步骤1创建的对象增加属性__proto__,将该属性链接至构造函数的原型对象
    3. 将步骤1创建的对象作为this的上下文
    4. 如果该函数没有返回对象,则返回this
      如果返回的是一个非对象,则返回this
      如果返回的是一个对象,则返回该对象
模拟实现一个new函数
js 复制代码
function NewFn(Fn) {
  if (typeof Fn !== 'function') {
    throw new Error('Fn must be a function')
  }
  const newObj = Object.create(Fn.prototype);
  const result = Fn.apply(newObj, Array.prototype.slice.call(arguments, 1))
  if (result && typeof result === 'object') {
    return result;
  } else {
    return newObj;
  }
}
模拟实现Object.create
js 复制代码
function inherit(p,properties) {
  if (p == null) throw TypeError();
  if (Object.create) {
    return Object.create(p)
  }
  const t = typeof p;
  if (t !== 'object' && t !== 'function') throw TypeError();
  // - 返回了一个对象;
  // - 这个对象的原型,指向了这个函数 Function 的 prototype;
  function F() {};
  F.prototype = p;
  if(properties) {
      Object.defineProperties(F, properties);
  }
  return new F();
}
new Object()和直接{}的区别

从创建对象的过程来讲,这两者底层实现基本是没有区别的。但是new Object()本质上是方法(只不过这个方法是内置的)调用, 既然是方法调用,就涉及到在proto链中遍历该方法,当找到该方法后,又会生产方法调用必须的堆栈信息,方法调用结束后,还要释放该堆栈。

所以,相比来说,更推荐直接字面量创建,更简洁,更高效

Object.create(null)和直接{}的区别

Object.create(null)创建了一个空对象,无Object原型对象的任何属性,可以自己写对应的方法等,不会污染全局Object

  • Object.create(null)使用
  1. 在我们使用for...in循环的时候会遍历对象原型链上的属性,使用create(null)就不必再对属性进行检查了
  2. 你需要一个非常干净且高度可定制的对象当做数据字典的时候
  3. 减少hasOwnProperty造成的性能损失并且可以偷懒少些一点代码的时候
继承
  • 子类的原型对象------类继承
js 复制代码
// 声明父类
function FatherClass() {
  this.children = [];
}
// 为父类添加共有方法
FatherClass.prototype.sayName = function () {
  console.log('I am father');
}
// 声明子类
function ChildClass() {
  this.hasFather = true;
}
// 继承父类
ChildClass.prototype = new FatherClass();
// 为子类添加共有方法
ChildClass.prototype.sayName = function () {
  console.log('I am children');
}

const child1 = new ChildClass();
const child2 = new ChildClass();
child2.children.push(12);
console.log(child1.children, child2.children); // 都为[12]
/**
 * 问题
 * 1. 传参
 * 2. 如果属性是引用属性,一旦某个实例修改了这个属性,那么都会被修改掉
 */
  • 构造函数继承
js 复制代码
// 声明父类
function FatherClass() {
  this.children = [];
}
// 为父类添加共有方法
FatherClass.prototype.sayName = function () {
  console.log('I am father');
}
// 声明子类
function ChildClass(arg) {
  this.hasFather = true;
  FatherClass.call(this, Array.prototype.slice.call(arguments, 1))
}
// 为子类添加共有方法
ChildClass.prototype.sayName = function () {
  console.log('I am children');
}

const child1 = new ChildClass('');
const child2 = new ChildClass('');
child2.children.push(12);
console.log(child1.children, child2.children); // [],[12]
/**
 * 问题
 * 1. 这种类型的继承没有涉及原型prototype,所以父类的原型属性或者方法不能被继承,想要被继承的话,只能在构造函数中定义,违背了代码复用原则
 * 2. 如果方法在构造函数中定义,每次都会被创建。
 */
  • 组合继承
js 复制代码
// 声明父类
function FatherClass() {
  this.children = [];
}
// 为父类添加共有方法
FatherClass.prototype.sayName = function () {
  console.log('I am father');
}
// 声明子类
function ChildClass(arg) {
  this.hasFather = true;
  FatherClass.call(this, Array.prototype.slice.call(arguments, 1))
}
// 只是想要一个原型链,又调用了一遍父类构造函数
ChildClass.prototype = new FatherClass();
// 为子类添加共有方法
ChildClass.prototype.sayName = function () {
  console.log('I am children');
}

const child1 = new ChildClass('');
const child2 = new ChildClass('');
child2.children.push(12);
console.log(child1.children, child2.children); // [],[12]
  • 原型式继承
js 复制代码
function inheritObj(obj){
  if(p == null) throw TypeError();
    if(Object.create) {
        return Object.create(p)
    }

    var t = typeof p;
    if(t !== 'object' && t !== 'function') throw TypeError();

  // 声明一个过渡函数对象
  function Fn(){};
  // 过渡对象原型继承父类对象
  Fn.prototype = obj;
  // 返回该过渡对象实例,该实例的原型链继承了父对象
  return new Fn();
}
  • 寄生组合式继承
js 复制代码
function inheritPrototype(parentClass, childClass) {
  // 复制一根父类的原型副本保存在变量中
  const p = inheritObj(parentClass.prototype);
  // 修正因为重写导致子类的constructor属性被修改
  p.constructor = childClass;
  // 设置子类的原型
  childClass.prototype = p;
}

// 声明父类
function FatherClass() {
  this.children = [];
}
// 为父类添加共有方法
FatherClass.prototype.sayName = function () {
  console.log('I am father');
}
// 声明子类
function ChildClass(arg) {
  this.hasFather = true;
  FatherClass.call(this, Array.prototype.slice.call(arguments, 1))
}
inheritPrototype(FatherClass, ChildClass);
// 为子类添加共有方法
ChildClass.prototype.sayName = function () {
  console.log('I am children');
}

const child1 = new ChildClass('');
const child2 = new ChildClass('');
child2.children.push(12);
console.log(child1.children, child2.children); // [],[12]
js 复制代码
/// class 继承中做的,而狭义上,组合寄生式继承,没有做的 //
for(var k in Person) {
    if(Person.hasOwnProperty(k) && !(k in Teacher)) {
        Teacher[k] = Person[k]
    }
}
//
// - class 继承,会继承静态属性
// - 子类中,必须在 constructor 调用 super, 因为子类自己的 this 对象,必须先通过 父类的构造函数完成。
POP面向过程编程Procedure-Oriented Programming

分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

OPP面向对象编程Object Oriented Programming

把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。

AOP面向切面编程Aspect Of Programming
相关推荐
zxctsclrjjjcph11 分钟前
【C语言】常见的C语言概念
c语言·开发语言
小灰灰爱代码15 分钟前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数模板来实现。
开发语言·c++·算法
Eiceblue22 分钟前
Python 复制Excel 中的行、列、单元格
开发语言·python·excel
项目題供诗26 分钟前
尚品汇-秒杀商品存入缓存、Redis发布订阅实现状态位(五十一)
开发语言·php
好名字082127 分钟前
monorepo基础搭建教程(从0到1 pnpm+monorepo+vue)
前端·javascript
m0_7145902634 分钟前
汇编(实现C语言程序的调用)
c语言·开发语言·汇编
做技术的Pandaer38 分钟前
Go 第二期
开发语言·golang
新知图书38 分钟前
Rust编程的作用域与所有权
开发语言·后端·rust
c#上位机1 小时前
C#事件的用法
java·javascript·c#
极客代码1 小时前
OpenCV Python 深度指南
开发语言·人工智能·python·opencv·计算机视觉