ECMAScript 6+ 新特性 ( 二 ) 面向对象

2.12.1.Object对象

2.12.1.1.Object.create()

Object.create() 是 JavaScript 中的一个内建函数,它提供了一种基于原型链的方式来创建新对象。该方法允许您指定一个现有对象作为新创建对象的原型(__proto__),并可选地定义新对象自身的可配置属性。以下是 Object.create() 方法的详细用法:

语法

Object.create(proto[, propertiesObject])

参数

  • proto (Object): 必需参数,表示新创建对象的原型对象。新对象的 [[Prototype]](内部属性,可通过 __proto__Object.getPrototypeOf() 访问)将被设置为这个对象。如果传入 null,新对象将没有原型,也就是说它的原型链到头,不会从任何对象继承属性和方法。

  • propertiesObject (Object): 可选参数,一个描述新对象初始属性的可枚举属性名到属性描述符映射的对象。这些属性会直接添加到新创建的对象上,而非其原型。每个属性描述符是一个包含以下属性的对象:

    • value: 属性的初始值。
    • writable: 布尔值,表示属性是否可写。
    • enumerable: 布尔值,表示属性是否可枚举(出现在 for...in 循环中)。
    • configurable: 布尔值,表示属性是否可配置(能否被删除或改变属性描述符)。
    • get: 一个函数,作为属性的 getter,在访问该属性时被调用。
    • set: 一个函数,作为属性的 setter,在设置该属性时被调用。

    如果省略此参数或传入 nullundefined,新创建的对象将没有任何自定义属性。

基本用法:仅指定原型对象

// 定义一个原型对象
const prototype = {
  name: 'John Doe',
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

// 使用 Object.create 创建新对象,继承原型对象
const person = Object.create(prototype);

// 新对象可以直接访问原型上的属性和方法
person.sayHello(); // 输出: Hello, my name is John Doe

添加初始属性

const prototype = {
  greet: function() {
    console.log('Greetings!');
  }
};

const person = Object.create(prototype, {
  name: {
    value: 'Alice',
    writable: true,
    enumerable: true,
    configurable: true
  },
  age: {
    value: 30,
    writable: true,
    enumerable: true,
    configurable: true
  }
});

console.log(person.name); // 输出: Alice
console.log(person.age); // 输出: 30
person.greet(); // 输出: Greetings!

使用 null 创建无原型对象

const objectWithoutPrototype = Object.create(null);
console.log(Object.getPrototypeOf(objectWithoutPrototype) === null); // 输出: true

特性和用途

  • 原型继承Object.create() 提供了一种更直接的方式来实现基于原型的继承,无需构造函数或 new 操作符,减少了对全局作用域的污染。

  • 隔离原型:通过指定不同的原型对象,可以轻松创建多个对象共享相同的接口(方法)但数据独立,避免了构造函数继承中子类原型修改会影响到所有子对象的问题。

  • 属性控制:通过 propertiesObject 参数,可以在创建对象时精细控制属性的行为,如是否可写、可枚举、可配置,以及定义 getter 和 setter。

  • 兼容性处理:在旧版本浏览器中,可以通过 polyfill 实现 Object.create() 的功能,确保代码的跨环境兼容性。

注意事项

  • 使用 Object.create() 创建的对象,其原型链上的属性查找遵循原型链规则。这意味着,如果在新对象上调用一个方法或访问一个属性,且该属性在新对象自身上未定义,JavaScript 会沿着原型链向上查找。

  • 若传给 Object.create()propertiesObject 参数中的某个属性名与原型对象上的同名属性冲突,新创建对象上的同名属性会遮蔽原型上的属性。

综上所述,Object.create() 是一种强大的对象创建工具,它不仅简化了基于原型的继承过程,还提供了对新对象属性行为的细粒度控制,是实现面向对象编程和设计模式的重要手段之一。

2.12.1.2.( ES 8 )属性的描述

Object.getOwnPropertyDescriptors()该方法返回指定对象所有自身属性的描述对象

数据属性:value(值)、writable(是否可写)、enumerable(是否可枚举)、configurable(是否可配置)

//声明对象
const STU = {
    name : "王小二",
    study : ['java','html','js']
};

console.log(Object.getOwnPropertyDescriptors(STU));
// 输出 : 
// { 
//     name: { value: '王小二', writable: true, enumerable: true, configurable: true }, 
//     study: { value: [ 'java', 'html', 'js' ], writable: true, enumerable: true, configurable: true }
// }
2.12.1.3.Object.setPrototypeOf()

在 JavaScript 中,__proto__ 和 Object.setPrototypeOf() 都是用来操作对象的原型链的。
__proto__ 是对象的一个内部属性,用于获取或设置对象的原型。可以通过访问器属性的方式来获取或设置 __proto__

const obj = {};
const proto = { hello: "world" };
obj.__proto__ = proto; // 设置 obj 的原型为 proto
console.log(obj.hello); // 输出 "world"

需要注意的是,__proto__ 属性在 ECMAScript 6 标准中被弃用,虽然现代浏览器仍然支持它,但不建议在生产环境中使用它。
因此,推荐使用 Object.setPrototypeOf() 方法来设置对象的原型。
Object.setPrototypeOf() 是一个静态方法,用于设置一个对象的原型。它接收两个参数:要设置原型的对象和要设置的原型对象。

Object.setPrototypeOf() 方法会将第一个参数对象的原型设置为第二个参数的对象。
这样,第一个参数对象就能够继承第二个参数对象的属性和方法。
需要注意的是,频繁地改变对象的原型链会对性能产生负面影响。因此,除非必要,一般不建议经常性地改变对象的原型。

const obj = {};
const proto = { hello: "world" };
Object.setPrototypeOf(obj, proto); // 设置 obj 的原型为 proto
console.log(obj.hello); // 输出 "world"
2.12.1.4.Object.getPrototypeOf()

在 JavaScript 中,Object.getPrototypeOf() 是一个静态方法,用于获取指定对象的原型。
Object.getPrototypeOf() 方法接收一个参数,即要获取原型的对象。它会返回指定对象的原型。

const obj = {};
const proto = { hello: "world" };
Object.setPrototypeOf(obj, proto); // 设置 obj 的原型为 proto
const objPrototype = Object.getPrototypeOf(obj);
console.log(objPrototype); // 输出 { hello: "world" }

在上面的示例中,我们使用 Object.setPrototypeOf() 方法将 obj 的原型设置为 proto。
然后,通过 Object.getPrototypeOf() 方法获取了 obj 的原型,并将其赋值给 objPrototype。
需要注意的是,Object.getPrototypeOf() 方法返回的是指定对象的原型,即它的 [[Prototype]] 属性的值。
如果指定对象没有明确的原型,即它是通过 Object.create(null) 或者 null 构造的对象,
那么 Object.getPrototypeOf() 方法会返回 null。
Object.getPrototypeOf() 方法的应用场景包括,
判断对象是否继承了指定的原型,检查对象的原型链关系,以及执行一些基于对象原型的操作。

2.12.1.5.原型链

原型链是JavaScript中实现继承的核心机制,它描述了对象间的继承关系是如何通过对象的内部属性[[Prototype]](在大多数环境中可通过__proto__属性或Object.getPrototypeOf()方法间接访问)连接起来的结构。具体特点如下:

  1. 对象关联:每个JavaScript对象(除了null)都有一个内部的[[Prototype]]引用,指向其原型对象。原型对象也是一个对象,同样可以有自己的原型,从而形成一个链条。
  2. 属性查找:当试图访问一个对象的属性或方法时,如果该对象本身没有定义该属性,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的顶端(通常是null)。
  3. 属性共享:原型链使得对象能够继承其原型对象的属性和方法,实现了属性和方法的复用,避免了在每个对象上重复定义相同的属性。
  4. 动态性:原型链是动态的,可以在运行时修改,如更改对象的原型、添加或删除原型上的属性,这些变化会立即反映到依赖该原型的对象上。
2.12.1.6.( (ES5) Object.defineProperty()

这是一个JavaScript原生方法,用于精确添加或修改对象的属性描述符(property descriptor)。

Object.defineProperty()方法接受三个参数:

Javascript

1Object.defineProperty(object, property, descriptor)
  • object: 要在其上定义或修改属性的对象。

  • property: 要定义或修改的属性的名称(字符串)。

  • descriptor
    

    : 描述符对象,定义了属性的行为。它是一个具有以下可选键的对象:

    • value: 该属性的值。

    • writable: 表示该属性是否可写,默认为false

    • enumerable: 表示该属性是否可枚举(例如在for...in循环或Object.keys()中),默认为false

    • configurable: 表示能否通过delete删除属性,或者能否再次修改属性描述符, 默认为false

    • get: 一个函数,作为属性的 getter 方法。

    • set: 一个函数,作为属性的 setter 方法。

let obj = {};

Object.defineProperty(obj, 'property1', {
    value: 42,
    writable: true,
    enumerable: true,
    configurable: true
});

// 定义一个具有getter和setter的属性
Object.defineProperty(obj, 'property2', {
    get: function () {

        return this._internalValue;
    },
    set: function (newValue) {
        this._internalValue = newValue * 2;
    },
    enumerable: true,
    configurable: true
});

obj.property1 = 24; // 可以赋值,因为writable为true
console.log(obj.property1); // 输出: 24

obj.property2 = 10;
console.log(obj.property2); // 输出: 20,因为setter方法翻倍了输入值

2.12.2.class类

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

// ES 5
// 人员 : 相当于 构造方法
function Person(name, sex){
    this.name = name;
    this.sex = sex;
}

//添加方法
Person.prototype.sayHi = function(){
    console.log("大家好!!");
}

//实例化对象
let wang = new Person('王小二', '男');
wang.sayHi();
console.log(wang);



// ES 6
//class
class Emp{
    //构造方法 名字不能修改
    constructor(name, sex){
        this.name = name;
        this.sex = sex;
    }

    //方法必须使用该语法
    sayHi(){
        console.log("大家好!!");
    }
}

let li = new Emp("李小三", '女');
li.sayHi()
console.log(li);
2.12.2.1.set/get 方法

在name属性之前添加了get和set关键字,这样就创建了一个名为name的访问器属性。get方法用于获取该属性的值,set方法用于设置新的值。

需要注意的是,使用属性访问器时,实际的属性名会在内部使用一个带下划线的变量名来存储。这是一种常见的命名约定,用于区分属性访问器和实际存储属性的变量。

 class Stu {
     constructor(name) {
         this._name = name;
     }

     get name() {
         console.log('读取 name 属性')
         return this._name;
     }

     set name(newName) {
         console.log('修改 name 属性');
         this._name = newName;
     }
 }

const stu = new Stu('王小二');
console.log(stu.name); // 输出 "王小二"

stu.name = '李小三';
console.log(stu.name); // 输出 "李小三"
2.12.2.2.静态

static 属于 类不属于 对象

// ES 5

// function Stu(){
//
// }

// 下面定义的属性方法属性 Stu , 相当于 静态的
// name 属性 , 比较特殊 为  Stu
// Stu.age = 12;
// Stu.sayHello = function(){
//     console.log("hello, 我是王小二");
// }
// console.log(Stu.age)
// Stu.sayHello();

// Stu.prototype.sex = '男';
//
// let wang = new Stu();
// //  这些属性是属于 Phone 的, 而不是属于 wang 的
// console.log(wang.name);
// wang.sayHello();
// console.log(wang.sex);


// ES 6
class Stu{
    //静态属性
    static name = '李小三';
    static sayHello(){
        console.log("hello, 我是李小三");
    }
}

let li = new Stu();
console.log(li.name);
// li.sayHello();
console.log(Stu.name);
Stu.sayHello();
2.12.2.3.ES 5 构造继承
// ES 5
// 人员 父类
function Person(name, sex){
    this.name = name;
    this.sex = sex;
}

//添加方法
Person.prototype.sayHi = function(){
    console.log("大家好!!");
}

//员工 子类 , 增加了 salary 薪水 属性
function Employee(name, sex, salary){
    // 调用父类的构造函数, 将自己及属性值 传入
    Person.call(this, name, sex);
    this.salary = salary;
}

//设置子级构造函数的原型
Employee.prototype = new Person;
Employee.prototype.constructor = Employee;

//声明子类的方法
Employee.prototype.eat = function(){
    console.log("去食堂")
}



const wang = new Employee('王小二', '男',6499 );

console.log(wang);
console.log(wang.name);
console.log(wang.sex);
console.log(wang.salary);
wang.sayHi();
wang.eat()

在这段代码中,EmployeePerson 的子类。以下是这两句代码的含义:

  1. Employee.prototype = new Person; 这行代码的作用是将 Person 构造函数的一个实例赋值给 Employee 原型对象。通过这样做,所有 Employee 类的实例都将继承 Person 类的所有方法和属性(通过原型链)。在这里调用 new Person 时并没有传入参数,因此新创建的 Person 实例的 namesex 属性将是 undefined。不过,在接下来的 Employee 构造函数内部已经通过 Person.call(this, name, sex) 正确地设置了这些属性。
  2. Employee.prototype.constructor = Employee; 在 JavaScript 中,每个构造函数的原型对象都有一个内置的 constructor 属性,它指向该构造函数本身。但是,当执行了 Employee.prototype = new Person 后,Employee 原型对象的 constructor 被修改为指向 Person。为了修复这一问题,并确保 Employee 的实例能够正确识别其构造函数,需要手动设置 Employee.prototype.constructorEmployee。这样做的目的是在后续可能涉及检查对象构造函数的场景下(如 instanceof 操作符或 .constructor 属性),能正确识别出对象是由哪个构造函数创建的。
2.12.2.4.ES 6 构造继承
// ES 6
class Person {
    //构造方法
    constructor(name, sex){
        this.name = name;
        this.sex = sex;
    }

    //父类的成员方法
    sayHi(){
        console.log("大家好!!");
    }
}

class Employee extends Person {
    //构造方法
    constructor(name, sex, salary) {
        super(name, sex);// Phone.call(this, brand, price)
        this.salary = salary;
    }

    eat() {
        console.log("去食堂")
    }

    // sayHi(){
    //     console.log("大家好!!我转正了");
    // }
}

const li = new Employee('李小三', '女', 5799 );
console.log(li);
console.log(li.name);
console.log(li.sex);
console.log(li.salary);
li.sayHi();
li.eat()
2.12.2.5.( ES 11 )私有属性

通过 在属性前加 # 来设置私有的属性,

在内部可以直接使用( 如: info() ),

在外部直接调用 会报错 , Uncaught SyntaxError: Private field ‘#age’ must be declared in an enclosing class

class Person{
     //公有属性
     name;
     //私有属性
     #age;
     #weight;
     //构造方法
     constructor(name, age, weight){
         this.name = name;
         this.#age = age;
         this.#weight = weight;
     }

     info(){
         console.log(this.name);
         console.log(this.#age);
         console.log(this.#weight);
     }
 }

//实例化
const girl = new Person('李小三', 18, '45kg');

console.log(girl.name); // 李小三
console.log(girl.#age); // Uncaught SyntaxError: Private field '#age' must be declared in an enclosing class
console.log(girl.#weight); // Uncaught SyntaxError: Private field '#weight' must be declared in an enclosing class

girl.info();
  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值