js三座大山之对象,继承,类,原型链

js三座大山

一:js三座大山之函数1 & js三座大山之函数2-作用域与动态this 即函数式编程。
二:js三座大山之对象,继承,类,原型链即面向对象编程
三:异步和单线程,目前主流是以promise为代表的异步编程

对象object

如何理解对象:js中可以从多个角度看待对象

– 对象是键值对:

有key/value:key只能是字符串和symbol,值可以是如何类型。
例子:

const obj = {};
obj[1] = '1111';      // 转化字符串’1‘赋值
obj['1'] = '22222';   // 覆盖’1‘ 
obj[null] = '33333';  // 转化为字符串`null`
obj[undefined] = '444444'; // 转化为字符串`undefined`
obj[Symbol('1')] = '5555'; // symbol 作为key
obj[{}] = '66666'; // 转化为字符串{}.toString() = Object.prototype.toString() = [object Object]
obj.name = '77777'; // ’name‘
obj[{age: '18'}] = '88888'; // {age: '18'} 转化为字符串 Object.prototype.toString() = [object Object]
obj[[]] = '99999'; // 转化为字符串[].toString() = ''


obj结果:
{
"": "99999"
1: "22222"
[object Object]: "88888"
name: "77777"
null: "33333"
undefined: "444444"
Symbol(1): "5555"
}

打印对象所有key
Reflect.ownKeys(obj).map(item=> typeof item)
// ['string', 'string', 'string', 'string', 'string', 'string', 'symbol']
key只有字符串和symbol两种类型。

属性描述符:

数据描述符value描述属性值 writable属性值是否可以更改。
访问器描述符getter读取属性时会走到getter方法 obj.a || obj[‘a’]. setter设置属性时会走到setter方法 obj.a = 12.
数据描述符和访问器描述符两者不可同时设置
通用描述符configurable当设置为false.属性描述符不能变更类型即不能从数据描述符变更为访问器描述符,同时属性不可删除。enumerable 属性是否可枚举。

枚举和迭代的区别。

遇到过很多同学会混淆这两个概念 这里给一个明确的区分。

  • 1.可枚举(enumerable):
    这是一个对象属性的特性,表示该属性是否可以通过某些操作被枚举出来。如果一个属性的enumerable特性为true,那么它可以被for...in循环和Object.keys()方法枚举出来。你可以通过Object.defineProperty()Object.defineProperties()方法来设置属性的enumerable特性。
  • 2.可迭代(iterable):
    这是一个对象的特性,表示该对象是否可以被迭代,即是否可以用for...of循环遍历。一个对象是可迭代的,需要实现一个名为Symbol.iterator的方法。这个方法返回一个迭代器对象,迭代器对象有一个next()方法,每次调用这个方法都会返回一个包含valuedone两个属性的对象。 例如数组是可以迭代的
    所以通常我们说的遍历 都是指迭代.
const a = [];   // 数组实现了迭代器
a[Symbol.iterator] // 返回迭代方法 ƒ values() { [native code] }

const b = {}; 
b[Symbol.iterator] // undefined 所以object不可迭代

const s = '';  // 字符串实现了迭代器
s[Symbol.iterator]  // 返回迭代方法 ƒ values() { [native code] }

js中与属性描述相关的方法主要是两类一种是获取的getOwnPropertyDescriptor 一种是修改的defineProperty。两个api在Object Proxy Reflect上都存在。也有批量的操作getOwnPropertyDescriptorsdefineProperties.
例子:

const obj = {
  name: "John"
};

// 获取属性描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, "name");
console.log(descriptor);
// 输出:{ value: 'John', writable: true, enumerable: true, configurable: true }
// 设置属性描述符
Object.defineProperty(obj, "name", {
    value: '123',
    writable: false,
});

console.log(Object.getOwnPropertyDescriptor(obj, "name"));
// 输出:{value: '123', writable: false, enumerable: true, configurable: true}

console.log(obj) // {name: '123'}
obj.name = '456'; // 不可写 所以无效
console.log(obj) // {name: '123'}

– 对象是实例:

在面向对象编程中,对象通常被视为类的实例。类定义了对象的结构和行为,而对象是这个定义的具体实现。例如,你可以定义一个“汽车”类,然后创建一个“汽车”对象,这个对象就是“汽车”类的一个实例。对象有实例属性 静态属性 原型属性
例子:

class Car{
    static config(){
        console.log('static config')
    }; 
    color='';
    constructor(c){
      this.color = c;  
    };
    run(){
        console.log(`${this.color} car run`)
    };
}

const c1 = new Car('red');
const c2 = new Car('blue');

打印c1: {color: 'red'}
打印c2: {color: 'blue'}
color 实例属性

获取原型Object.getPrototypeOf(c1) // {constructor: ƒ, run: ƒ}
run 原型属性

c1.config // undefined
Car.config // 打印函数 静态属性

类class

在JavaScript中,类(class)是一种特殊的函数,是对象的模板。它提供了一种更简洁和更直观的方式来创建对象和处理继承.
例如上面的例子。

es5:
function Car(c){
  this.color = c;
}
Car.prototype.run=function(){
  console.log(`${this.color} car run`)
}
Car.config=function(){
  console.log('static config')
}


es2015:
class Car{
    static config = function(){
      console.log('static config')
    }; 
    name='';
    constructor(c){
       this.color = c;  
    };
    run(){
       console.log(`${this.color} car run`)
    };
}

类实际是一个语法糖,下面是一个经过编译后的代码:

/**
 * 返回类型
 */
function _typeof(obj) {
    return; 
}
/**
 * 作为方法调用拦截 
 */
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError('Cannot call a class as a function');
    }
}

/***
 * 批量给对象添加属性 定义属性描述符
 */
function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ('value' in descriptor) descriptor.writable = true;
        Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
    }
}

/***
 * 将属性动态添加到原型上
 */
function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    Object.defineProperty(Constructor, 'prototype', { writable: false });
    return Constructor;
}

/**
 * 属性添加到实例上
 */
function _defineProperty(obj, key, value) {
    obj[key] = value;
    return obj;
}
/**
 * 将输入转化为一个合法的属性类型(string | symbol)
 */
function _toPropertyKey(arg) {
    return;
}
var Car = (function () {
    function Car(c) {
        _classCallCheck(this, Car); // 拦截作为普通函数调用
        _defineProperty(this, 'name', ''); // 添加实例属性
        this.color = c; // 添加实例属性
    }
    _createClass(Car, [
        {
            key: 'run',
            value: function run() {
                console.log(''.concat(this.color, ' car run'));
            },
        },
    ]);
    return Car;
})();
_defineProperty(Car, 'config', function () {
    console.log('static config');
});

实际上就是上面的es5实现而已~

继承extend

js中实现继承的方式非常的多 根据需求场景可以采用不同的方法。
目的都是为了安全合理的获取父类的属性和方法

通过class方式

class Car{
    static config(){
        console.log('static config')
    };
    lights=[];
    color='';
    constructor(c){
      this.color = c;  
    };
    run(){
        console.log(`${this.color} car run`)
    };
}

class Maybach extends Car{
    values(){
        console.log('价格贵')
    }
}

const m = new Maybach('green')
const m1 = new Maybach('green')
console.log(m.color) // 继承color属性 green
console.log(m.run()) // 继承run方法 green car run
console.log(m.values()) // 实现了新定义的方法 价格贵
m.lights.push(1)
console.log(m.lights) // [1] //继承的属性为私有的 不会影响其他实例
console.log(m1.lights) // []

构造方法继承

function Car(c){
  this.color = c;
  this.show=function(){}
}
Car.prototype.run=function(){
  console.log(`show car`)
}

function Maybach (c, prize) {
    Car.call(this, c); // 继承实例属性
}
const m = new Maybach('red', '1000w')
m.run // undefined
m.show // 打印函数

缺点:方法只能写在构造函数里面,原型上的方法无法被继承,原型的存在就是为了继承和js的设计初衷相违背了。

原型继承

function Car(){
  this.lights = [1,2];
  this.show=function(){}
}
Car.prototype.run=function(){
  console.log(`show car`)
}

function Maybach (prize) {
  this.prize = prize;
}
Maybach.prototype = new Car();
const m1 = new Maybach('1000w')
const m2 = new Maybach('900w')
m1.lights.push(3)
m1.lights // [1,2,3]
m2.lights // [1,2,3]

缺点:所有实例都访问同一个原型对象,引用类型的属性被共享了,无法做到实例隔离。

组合继承

通过将构造方法继承继承和原型继承合并在一起,避免了他们的缺点。既可以继承原型的方法 也可以做到引用类型实例隔离。

function Car(){
  this.lights = [1,2];
  this.show=function(){}
}
Car.prototype.run=function(){
  console.log(`show car`)
}

function Maybach (prize) {
    Car.call(this); // 实例属性
    this.prize = prize;

}
Maybach.prototype = new Car() // 原型继承
const m1 = new Maybach('1000w')
const m2 = new Maybach('900w')
m1.lights.push(3)
m1.lights // [1,2,3]
m2.lights // [1,2]

缺点:原型对象上还是会有car的属性,但永远不会访问到。会有冗余。
可以进一步优化

Maybach.prototype = new Car() // 这里修改一下 不在直接new Car  
Maybach.prototype = Car.prototype // 额... 这样更不行 父子同一个原型 子改父也会被改变

所以我们需要借助一个中间函数
var Fn = function(){}
Fn.prototype = Car.prototype;
Maybach.prototype = new Fn()

寄生组合继承

优化后的继承会更好一些。

function Car(){
  this.lights = [1,2];
  this.show=function(){}
}
Car.prototype.run=function(){
  console.log(`show car`)
}

function Maybach (prize) {
    Car.call(this); // 实例属性
    this.prize = prize;
}
var Fn = function(){}
Fn.prototype = Car.prototype;
Maybach.prototype = new Fn(); // 空白的对象实例 实现原型继承

在JavaScript中,继承是通过原型链来实现的,当试图访问一个对象的属性时,如果该对象自身没有这个属性,JavaScript会沿着原型链去查找这个属性。

原型&原型链prototype

要想画出正确的原型链 本质上就是在解释 对象 原型 构造函数三者的关系 需要明确以下几个规则。
1:任何对象都是由函数创建的
2:只有函数才拥有原型对象
3:函数是可执行的对象
4:对象有__proto__指向原型对象、有constructor属性指向构造函数

js三座大山之对象,继承,类,原型链

原型链有两个终点:

作为对象的终点是object.prototype然后是null;
作为函数的终点是function。

参考:

  1. developer.mozilla.org/zh-CN/docs/…
  2. developer.mozilla.org/zh-CN/docs/…
  3. es6.ruanyifeng.com/#docs/class
  4. juejin.cn/post/684490…
  5. blog.jerrychu.top/2021/08/30/…

原文链接:https://juejin.cn/post/7314233955372564515 作者:Do

(0)
上一篇 2023年12月20日 下午4:27
下一篇 2023年12月20日 下午4:37

相关推荐

发表回复

登录后才能评论