JS 面向对象

数据类型与对象

对于JS中的数据类型,其本质都是基于各自的对象类进行创建的,因此如果我们可以通过修改这个对象类的原型来对某个数据类型整体都进行修改

数据类型对象

Number

数字,例如我们给数字类型都添加一个方法判断是否为正数:

Number.prototype.isPos = function() {
  return this > 0;
}
let b = 1;
b.isPos();
// true
Boolean
String
RegExp
Array
Function
参考

https://www.runoob.com/jsref/jsref-obj-array.html

对象API

Object.create

创建对象,第一个参数指定对象原型,第二个参数为对象本身,举例:

a = {x:1, y:2}
b = Object.create(a, {})
b.__proto__ === a
// true
Object.setPrototypeOf

设置原型,举例:

let a = {};
let b = {};
console.log(a.__proto__ === Object.prototype)  // true
Object.setPrototypeOf(a, b);
// 将a的原型指向b
console.log(a.__proto__ === Object.prototype)  // false
console.log(a.__proto__ === b)  // true
Object.getPrototypeOf

查看原型,举例:

Object.getPrototypeOf({});
// {}
Object.getOwnPropertyDescriptor

获取对象当中指定属性的特征,举例:

let a = {x:1};
console.log(Object.getOwnPropertyDescriptor(a, "x"));
// { value: 1, writable: true, enumerable: true, configurable: true }
// 几个属性分别表示:值、是否可修改、是否可遍历、是否可以修改属性特征
Object.defineProperty

定义、修改属性特征,举例:

let a = {};
Object.defineProperty(a, "x", {
    value: 1,
    writable: true,
    enumerable: false
})
a.x = 2;
console.log(a.x)
// 2
console.log(Object.keys(a));
// []

可以看出属性x可修改,但不可遍历
注:
如果定义的属性设置了访问器,则不能设置valuewritable属性,举例:

let o = {};
Object.defineProperty(o, "a", {
    enumerable: true,
    configurable: true,
    // 如果设置value或writable属性会报错
    set(val) {
      this._a = val;
      console.log(val);
    },
    get() {
      return this._a;
    }
});
console.log(o.a);
o.a = 100;
console.log(o.a);

// undefined
// 100
// 100
Object.preventExtensions

不允许对象添加属性,举例:

let a = {};
a.x = 1
Object.preventExtensions(a);
a.y = 2;
console.log(Object.keys(a));
// [ 'x' ]

可以看出设置不允许添加属性以后,属性y就没有被添加进去了
注:
可以通过Object.isExtensible查看对象是否允许添加属性

Object.seal

封闭属性,不允许添加、修改、重新定义属性(configurable变成false)
注:
可以通过Object.isSealed查看对象是否冻结

Object.freeze

对象冻结,无法改变、添加、删除对象内容(writable、configurable都变成false)
注:
可以通过Object.isFrozen查看对象是否冻结

参考:https://www.cnblogs.com/amiezhang/p/11312973.html

Object.keys()/Object.values()

访问对象下所有可迭代的key/value,举例:

a = {x:1, y:2}
Object.keys(a)
// ["x", "y"]
Object.is

判断对象是否为某个类型,举例:

Object.is("aaa", NaN)

注:
NaN类型需要用Number.isNaN或者Object.is来判断

Object.assign

将后面几个对象的内容合并到第一个对象里(重复属性名,后面的会覆盖前面的),举例:

console.log(Object.assign({x:1}, {y:2}))
// { x: 1, y: 2 }

我们可以用该方法实现浅拷贝,举例:

let a = {x:1};
let b = a;
let c = Object.assign({}, a);
// 将a合并到空对象里,从而实现浅拷贝
a.x = 2;
console.log(b.x, c.x);
// 2 1

可以看到此时c和a并不是同一个对象

对象操作

变量命名key

如果希望key的值是由变量决定,则可以通过[变量]来实现,举例:

a = "aaa";
b = {
    [a]: 1
}
console.log(b)
遍历可迭代对象
遍历key
a = {x:1, y:2};
for(let k in a) {
  console.log(k);
}

// x
// y

遍历数组时,获取的则是下标,举例:

a = ["a","b","c","d"];
for (let k in a){
  console.log(k);
}
// 0
// 1
// 2
// 3
遍历value
a = ["a","b","c","d"];
for (let v of a){
  console.log(v);
}
// a
// b
// c
// d
instanceof

判断是否是某个类实例(本质是看是否prototype是否在其原型链上),举例:

[] instanceof Array
hasOwnProperty

当前对象是否含有某个属性(不包括原型链上的),举例:

let a = {x:1};
a.__proto__.y = 1;
console.log(a.hasOwnProperty("x"))  // true
console.log(a.hasOwnProperty("y"))  // false
attr in o

当前对象是否含有某个属性(包括原型链),举例:

let a = {x:1};
a.__proto__.y = 1;
console.log("x" in  a)  // true
console.log("y" in  a)  // true
访问器

在对象中当要访问某个属性时,可以进行自定义操作,举例:

let a = {
    data: {
        x: 1
    },
    // 当设置x值的时候触发
    set x(val) {
        console.log("将x设置为:", val);
        this.data.x = val;
    },
    // 当获取x值的时候触发
    get x() {
        console.log("获取x的值");
        return this.data.x;
    }
}

a.x = 100;
console.log(a.x);
// 将x设置为: 100
// 获取x的值
// 100

定义

ES6提供了类相关的关键字用于创建里,主要有:class(定义类)、constructor(构造器)、extends(继承)、super(父类),static(定义静态属性/方法)举例:

class User {
    constructor(name, pwd) {
        // 构造方法
        this.name = name;
        this.pwd = pwd;
    }
    showName() {
        // 直接输入方法名定义方法
        console.log(this.name);
    }
    static showRule() {
        // 定义静态方法,可以从类直接调用而无需实例化
        console.log("this is User rule");
    }
    static rule = "this is rule";
    // 定义静态属性,如果报错可能是浏览器版本不支持,建议换成最新chrome尝试
}
class Vip extends User {
    // 继承父类
    constructor(name, pwd, level) {
        super(name, pwd);
        // 调用父类构造方法
        this.level = level;
    }
    showPwd() {
        console.log(this.pwd);
    }
    showLevel() {
        console.log(this.level);
    }
}
console.log(User.rule);
User.showRule();
// 直接从类调用静态方法
let user = new Vip("aaa", "111", "1");
user.showName();
user.showPwd();
user.showLevel();
  • 也可以通过直接在类中通过赋值来定义属性成员,举例:
class A {
    // 给实例设置私有成员,等价于在constructor中执行this.x = 1
    x = 1
    y = {a: 1}
    // 设置静态属性(@babel/plugin-proposal-class-properties里设置了关于这种语法的处理)
    static z = {a: 1}
}

let a = new A();
a.y.a = 2
A.z.a = 2
let a2 = new A();
console.log(a2.y.a);
console.log(A.z.a);
私有属性

前面加#即可,私有方法可以通过定义私有属性的值为函数即可,举例:

class A {
  #a = () => {};
}

深拷贝/浅拷贝

浅拷贝示例
a = {x:1};
// 实现浅拷贝a
// 第一种
b = { x: a.x };

// 第二种
b = {};
for (let key in a) {
  b[key] = a[key];
}

// 第三种
b = { ...a };

// 第四种
b = Object.assign({}, a);
深拷贝示例
  • 通过JSON序列化:
let o = {x:1};
let b = o
b === o
// true
let json = JSON.stringify(o)
let deepCopyO = JSON.parse(json)
deepCopyO === o
// false
  • JSON序列化只能操作基本类型,对于非基本类型,可以通过递归浅拷贝来实现
  • 实际开发中建议使用lodash相关库来进行深拷贝操作

lodash参考:
https://www.npmjs.com/package/lodash.clonedeep
https://github.com/lodash/lodash/tree/4.5.0-npm-packages/lodash.clonedeep

反射

参考:http://tech.dianwoda.com/2017/08/09/jsde-fan-she-xue-xi-he-ying-yong/

你可能感兴趣的:(JS 面向对象)