JavaScript面向对象知识点梳理

77 阅读3分钟

什么是面向对象?

对象:万物皆对象

类:对对象的归类和细分

实例:类中的具体成员

  • JS内置类:

    • 每一种内置的数据类型都有自己对应的内置类:Number、String、Object等。代码中的具体值就是对应的类的实例。例如10 -> Number -> Object
    • 每一个HTML元素标签:包括window/document等在JS中都有自己的内置类。例如div -> HTMLDivElement -> HTMLElement -> Element -> Node -> EventTarget -> Object

instanceof 检测某个实例是否属于这个类。局限性:不能识别原始值。

类型转换存在内部处理机制拆箱和装箱

console.log(10.toFixed(2)) // 10.00 10 -> Object(10)console.log(new Number(10) + 10// 20 Symbol.toPrimitive / valueOf / toString

hasOwnProperty 检测某个属性是否是这个对象的私有属性

var obj = {age20};
obj.hasOwnProperty('age'); // true
obj.hasOwnProperty('hasOwnProperty'); // false

in操作符 检测检测某个属性是否是这个对象的属性(无论私有还是公有)

对象循环 for in

for in 的缺陷

  • 不能迭代Symbol属性
  • 不一定按照自己编写的键值对顺序迭代(优先迭代数字属性,从小到大,再去迭代非数字属性「自己编写的顺序」)
  • 不仅会迭代对象的私有属性,对于一些自己扩展的公有属性也会迭代到「迭代可枚举的」
// 遍历数组 for循环 比 for in 循环好
let arr = [1020 ,30];
​
/**
for 循环的本质不是遍历数组,是自己控制一个循环的逻辑,只不过每一轮循环的值恰好和数组索引类似,可以直接作为下标使用,所以看起来是遍历数组。
*/
for (let i = 0; i < arr.length; i++) {
 console.log(arr[i], i);
}
// for in 本质是迭代对象,按照本身的键值对去一一迭代的
for (let key in arr) {
 console.log(arr[key], key);
}

Object.getOwnPropertySymbols 获取对象所有Symbols的属性

Object.keys || Object.getOwnPropertyNames 拿到所有非Symbol的私有属性

对象的属性类型只有两种 string || Symbol

原型和原型链

  • 大部分函数数据类型的值,都具备 prototype 属性,这个属性被称为原型属性,也称为显式原型。属性值本身是一个对象(浏览器会默认为其开辟一个堆内存,用来存储当前类所属实例可以调用的公共属性和方法),在浏览器默认开辟的这个堆内存中「原型对象」,有一个默认的属性constructor(构造器/构造器),属性值是当前类本身。

    • 函数数据类型有哪些?

      • 普通函数(实名函数、匿名函数)
      • 箭头函数
      • 构造函数/类
      • 生成器函数
      • ......
    • 不具备prototype属性的函数类型

      • 箭头函数(只有__proto__
      • 基于ES6给对象成员赋值函数的快捷操作 let obj = { fn() {} }
      • ......
  • 每一个对象数据类型都具备__proto__(原型链/隐式原型),属性值指向自己所属类的原型prototype

    • 有哪些对象数据类型?

      • 普通对象
      • 特殊对象,Array、Date、Math、Error等
      • 函数对象
      • 实例对象
      • 构造函数的prototype
      • ......
let arr = [123];
// arr 是 Array 类的实例,所以arr.__proto__ => Array.prototype;
// Array 原型的值也是一个对象,拥有自己的__proto__ 并指向Object.prototype
// Array.prototype.__proto__ => Object.prototype// 而Object是所有类的基类,所以Object.prototype.__proto__ 指向的也是Object自己,这样没有意义,所以Object.prototype.__proto__ 的值为null// 构造函数原型的方法相对于实例来说是公有的,相对于本身来说 是私有的方法
arr.hasOwnProperty('forEach'// false
Array.prototype.hasOwnProperty('forEach'// true

实现new

function new() {
   const Func = [].shift.call(arguments);
   let obj, result, proto = Func.prototype;
   if (Func === Symbol || Func === BigInt || typeof Func !== 'function' || !proto) {
       throw new TypeError(`${Func} is not constructor`);
  }
   obj = Object.create(proto);
   result = Func.apply(obj, arguments);
   if (typeof result === 'object') {
       return result;
  }
   return obj;
}

实现Object.create

Object.create = function create(prototype) { // 将参数作为创建的新对象的原型
   if (prototype !== null && typeof prototype !== 'object') {
       throw new TypeError(`${prototype} is Not Object or null`);
  }
   function _Proxy() {
​
  }
   _Proxy.prototype = prototype;
   return new _Proxy;
}

原型重定向

内置类原型不允许重定向,但可以对单一的属性或方法进行重写。

方法中的属性赋值都是赋值给私有的属性。

函数的多种角色

  • 函数

    • 普通函数(上下文、作用域)
    • 构造函数(类、实例、原型、原型链)
  • 普通对象(键值对)

function Fn() {};
​
Fn.__proto__ === Function.prototype// true
Function.__proto__ === Function.prototype// true
Object.__proto__ === Function.prototype// true
Object.__proto__.__proto__ = Object.prototype// trueObject instanceof Function // true
Function instanceof Object // trueFunction.prototype // ƒ () { [native code] }

所有对象(包含函数对象 )都是Object的实例,都可以调用Object原型的方法。

JS中所有的非基础类型的值都是对象的实例,但是函数比较特殊,即是对象,也是函数。

Function.prototype 是一个匿名空函数,但是没有prototype,所以还是把它当做和其他原型对象一样即可。