前端学习

前端基础整理 | Javascript基础 (二)对象与继承

2019-06-10  本文已影响3人  格致匠心

个人向,对JS知识进行了查漏补缺,主要来源于《JS高级程序设计》和网上博客,本文内容主要包括以下:

  1. 对象
  2. 创建对象
  3. 继承

一、对象

特性(attribute),描述了属性(property)的各种特征。内部使用,不能直接访问,两对方括号括起来。

1. 数据属性:

❗在使用defineProperty创建时候,未定义configurable / writable / enumerable都是默认false

2. 访问器属性

二、创建对象

1. 工厂模式:

function createPerson(name, age) {
  let o = new Object()
  o.name = name
  o.age = age
  o.sayName = function() { alert(o.name); }
  return o;
}

2. 构造函数模式

function Person(name,age){
  this.name=name
  this.age=age
  this.sayName = function() { alert(this.name); }
}

实际上,任何函数都可以是构造函数,只要配上new。而构造函数没有用new来调用,也是一个普通函数。

  1. 创建一个新对象
  2. 把构造函数的作用域赋给新对象(this指向新对象)
  3. 执行构造函数代码(给新对象添加属性)
  4. 返回新对象

手动模拟一下new的工作:

function newPerson(name, age) {
  let o = new Object()
  Person.call(o, name, age)
  return o
}
tony = newPerson('tony', 10)

3. 原型模式

person.hasOwnProperty('name') //true因为这是来自实例的属性。
person.hasOwnProperty('age') //false因为这是来自原型继承来的属性。
'age' in person //true 'in'操作符在对象能访问该属性时返回true
Object.keys(Person.prototype) //获取[[Enumerable]]为true的可枚举实例属性
Object.getOwnPropertyNames() //获取所有的实例属性

Reflect.ownKeys(person) // 获取所有的实例属性以及symbol
Object.getOwnPropertyNames(person).concat(Object.getOwnPropertySymbols(person)) // 就是上面代码的实际返回

4. 原型+构造函数模式

目前ECMAScript中最广泛的模式,就是构造函数模式用来定义实例属性,原型模式用来定义方法和共享的属性。

5. 动态原型模式

其实就是在构造函数内弄了一个判断语句,当不存在一个方法的时候,将方法挂在原型上。

function Person(name) {
  this.name = name
  if ( typeof this.sayName!='function') {
    Person.prototype.sayName = function() { alert(this.name)}
  }
}

6.寄生构造模式

代码和工厂模式一样,就是用new来创建,暂时不做分析

7.稳妥构造模式

有种闭包的感觉,不用this进行构造,没有公共属性,用在需要特殊的安全执行环境。

三、 继承

1. 原型链

MDN文档的描述再结合上面的"一张经典的图片"食用更佳:
原型链的顶端是Object,再往上就是null了,null没有原型。

JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

2. 借用构造函数继承

function Person(name){
  this.name = name
}
function Student(name){
  Person.call(this, name)
}

3. 原型链继承

function Person(name){
  this.name = name
}
person = new Person('tony')
function Student(){}
Student.prototype = person 
Student.prototype.constructor = Student

4. 原型链+构造函数 组合继承

function Person(name){
  this.name = name
}
Person.prototype.sayHi = function(){alert('hello')}
function Student(name){
  Person.call(this,name)
}
Student.prototype = new Person()
Student.prototype.constructor = Student

5. 原型式继承

function object(o){
  function F(){}
  F.prototype = o
  return new F()
}
let person = {
    name: 'tony'
};
let anotherPerson = object(person)

ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。

所以上述可以简化为 let anotherPerson = Object.create(person)

6. 寄生继承

只是一种思路而已,没什么优点,通过给使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力。

function createAnother(original){ 
    var clone=object(original)
    clone.sayHi = function(){
        alert("hi");
    };
    return clone
}
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person)
anotherPerson.sayHi()

7. 寄生组合式继承

目前最完美的继承方法,只需要在继承函数中调用构造函数再使用下面的继承就行了。

function inheritPrototype(subType, superType){
    var prototype = Object.create(superType.prototype); // 创建了父类原型的浅复制
    prototype.constructor = subType;             // 修正原型的构造函数
    subType.prototype = prototype;               // 将子类的原型替换为这个原型
}

为了方便理解,这里有两个类似的继承函数。第一个是使用类似原型构造的F函数,第二个是直观的展示了继承在Chrome等具有__proto__指针中的形式。

function F_inherits(Child, Parent) {
  var F = function() {}
  F.prototype = Parent.prototype
  Child.prototype = new F()
  Child.prototype.constructor = Child
}
function myInherits(Child, Parent) {
  Child.prototype = { constructor: Child, __proto__: Parent.prototype }
}

8. class 继承(ES6)

ES6继承的结果和寄生组合继承相似,本质上,ES6继承是一种语法糖。但是,寄生组合继承是先创建子类实例this对象,然后再对其增强;而ES6先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

class A {}
class B extends A {
  constructor() {
    super();
  }
}
class A {}
class B {}
Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}
// B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

ES6继承与ES5继承的异同:

四、一些自己实现的函数

帮助大家更好地理解:对象、继承。

/**
 * call实现
 */
Function.prototype._call = function(ctx, ...args) {
  ctx = ctx || window
  ctx.func = this
  let result = ctx.func(...args)
  delete ctx.func
  return result
}
/**
 * apply实现
 */
Function.prototype._apply = function(ctx, args) {
  ctx = ctx || window
  ctx.func = this
  let result = ctx.func(...args)
  delete ctx.func
  return result
}
/**
 * bind实现
 */
Function.prototype._bind = function(target) {
  target = target || window
  const that = this
  const args = [...arguments].slice(1)
  let fn = function() {
    return that.apply(
      this instanceof fn ? this : target,
      args.concat(...arguments)
    )
  }
  let F = function() {}
  F.prototype = this.prototype
  fn.prototype = new F() // fn.prototype.__proto__ == this.prototype  true
  return fn
}
/**
 * instanceof实现
 */
function _instanceof(a, b) {
  let prototype = b.prototype
  let a = a.__proto__
  while (true) {
    if (a === null || a === undefined) {
      return false
    } else if (a === prototype) {
      return true
    } else {
      a = a.__proto__
    }
  }
}
上一篇 下一篇

猜你喜欢

热点阅读