es6-es11系列3 对象

面向对象和面向过程

面向过程,考虑的是需求要拆解多少个步骤来实现;
面向对象,考虑的是这个需求里面有多少个对象,他们有哪些属性是必须的,行为之间的关系是如何的;
javascript 准确的说是基于对象(object- based)的语言

类与对象

类是对象的模版,定义了同一组对象共有的属性和方法,类可以有父类和子类。而对象就是对类进行实例化之后的结果,换句话说对象就是我们new出来的的实例。
构造函数内如果定义方法的话,最大的问题就是每次都会执行new Function的操作
例如:this.showName = function(alb){return a+b} 就等于 this. showName = new Function(a, b, ‘return a+b’)
正确的做法是应该把方法定义在原型(prototype)上。

静态属性和静态方法使用场景

比如定一个instanceCount的静态变量
再定一个getInstanceCount的静态方法
在constructor 中行进this.instanceCount ++
这样每次类被实例化的时候instanceCount的值就会+1,通过getInstanceCount则可以获得当前类已经被实例化了多少次

ES5 继承的几种方式

只继承父类的属性(构造函数继承),在子类的构造函数中调用父类的call方法把this传过去,例如

function Dog(name, color){
	Animal.call(this, name)
}

如果要继承父类的方法需要把子类的原型指向父类实例,并且把原型的构造函数指回来,这种方式叫原型继承,例如:

Dog.prototype = new Animal()
console.log(Dog.prototype.constructor) // function Animal() {this.name = 'animal'}
Dog.prototype.constructor = Dog // function Dog(name, color) {Animal.call(this, name)}

如果将上面的两种方法组合起来使用,则可以同时继承属性和方法,所以叫组合继承

ES6的继承

es6 使用extends继承class
在子类的constructor中使用super(name)的方式继承父类的属性,类似es5的构造函数继承
es6中定义顶层的属性的方式使用get语法 ,例如

get sex(){
	return ‘male’
}
let p1 = new Person(‘xx’)后

通过p1.sex获得
这种顶层属性不能被外部直接修改,比如p1.sex=111会报错
如果要改变sex的值,则需要使用set语法
优点是可以在属性设置和获取的时候做前置的拦截操作,例如:

constructor(){
	this._sex = -1
}
get sex(){
	if(this._sex ===1) return ‘male’
 	if(this._sex === 0) return ‘female’
}
// sex的值仅接受0和1
set sex(val){
	if(val ===0 || val ===1) this._sex = val
}

class只能用static定义的静态方法,不能定义属性,父类的静态方法可以被子类调用,但是不可以在实例下调用。
如何非要在类上面定义静态属性,可以直接写Person.count = 5 但是不推荐

Symbol

Symbol不是对象,不能被new,也不能添加属性和方法。

let s1 = Symbol(‘foo’) // 参数可以添加描述,可以调用s1.description来输出描述
let s2 = Symbol(obj) // 如果参数是对象则会自动调用对象下的toString(),可以可以手动在对象下面自定义一个toString方法让它自动被调用

如果使用Symbol.for定义的Symbol则可以相等。
let s1 = Symbol.for(‘foo’) 在全局定义个key为foo的Symbol,可以使用Symbol.keyfor(s1) 打印Symbol全局的key值

使用场景一,解决对象的key重名后覆盖问题

const stu1 = Symbol(‘tom’)
const stu2 = Symbol(‘tom’)
const data = {
	[stu1]: {address:’xxx’},
	[stu2]: {address:’xxx’},
}

场景二,使用Symbol来模拟私有属性

const s1 = Symbol(‘a’)
constructor(){
	this[s1] = 111
}
for in ,Obejct.keys(),Object.getOwnPropertySymbols都不能遍历Symbol属性
只有 用for(let key of Reflect.ownKeys(user))  可以

场景三,魔术字符串
const shapeType = {
	triangle: Symbol(), // 无需给定具体的字符串,比如:’triangle’
	circle: Symbol()
}
switch (shape){
	case shapeType.triangle:
	case shapeType.circle:
…
}

SET

基本语法

let s = new Set([1,1,2]) // [1,2]
s.add(5).delete(2).clear().size
s.has(1) // false

set 可以被forEach和for of遍历
求数组的交集

let s1 = new Set(arr1)
let s2 = new Set(arr2)
let result = new Set(s1.filtter(item => s2.has(item)))

weakSet // 只能添加对象

let ws = new weakSet()
ws.add(1) // 报错

weakSet不能使用forEach
弱引用,不会被垃圾回收机计数,当引用的对象消失后weakSet也会消失

map

map的key 跟对象不同,可以传对象和数组
常用的方法
set
get
has
size
delete
创建map的基本语法
new Map([‘name’,’tom’],[‘age’,12])
使用forEach for of 进行遍历

map优势
size has 方法更便捷性能更好 key不会跟原型的属性冲突
weakMap 的key只支持引用类型 不能遍历也没有size方法

应用场景

let el = document.get elements by tag name()
let els = new map()
els.set(el,’desc’)

用map存dom元素用key来遍历管理很方便
由于弱引用的机制 还能防治内存泄露

字符串

es5的unicode 取值范围是0000-ffff
es6 使用/u{}的方式增加了取值范围
\hhh 8进制字符
\x12 16进制字符
for of 遍历字符串

模版字符串可以直接换行 无需写+和\n
模版字符还支持函数方式,例如foo’aaa${123}bbb’

es6新增的方法
includes
startsWith
endsWith
repeat(7)

正则表达式

es5中正则中的修饰符
i 忽略大小写
m 多行
g 全局

es6新增两个修饰符
y 粘连修饰符
匹配失败后会返回到起始位置

u unicode 修饰符

数值

b0和 0o来表示十进制和8进制
5/0 会得到无穷大 infinity
‘a’ / 5 会得到 NaN not a number
Number.isFinite() 传入 字符串或布尔时也会返回false
Number.isNaN 注意此方法通常判断值是否是一个数字
es6中会把一些全局的方法放到对应的模块中,例如parseInt parseFloat
Number.isInteger() 是否是整数
Number.trunc 取整
Math.sign 判断是正数还是0还是负数
Math.cbrt 立方根

代理 Proxy

es5中对对象进行拦截使用 Object.defineProperty()
使用Proxy拦截对象,实现私有属性(下划线开头)禁止访问

let user = {
	name:’xxx’,
	age: 18,
	_gender: ‘male’	
}
user = new Proxy(user,{
	get(target, key) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key]
		}
	},
	set(target, key, val) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key] = val
		}
	}
	 has (target, key) {
                    if(key[0] === "_") { // key.startsWith(‘_’)
                        return false;
                    }
                    return key in target;
         }
	deleteProperty(key){
		if(key.startsWith(‘_’){
			throw new Error(‘不可删除’)
		} else {
			delete target[key]
			return ture	
		}
	}
	ownKeys(target){
		return Object.keys(target).filter(key => !key.startsWith(‘_’))
	}
})
使用Proxy拦截函数调用
let sum = (…args) =>{
	let num = 0
	args.forEach(item => {
		num += item
	})
	return num
}
sum = new Proxy(sum, {
	// 当前函数,上下文this,参数
	apply(target, ctx, agrs) {
		return target(…args) * 2
	}
})
// 使用Proxy拦截类的构造函数
let User = class {
	constructor (name){
		this.name = name
	}
}
User = new Proxy(User, {
	// 当前类,参数,目标类
	constructor(target, args, newTarget){
		return new target(…args)
	}
})

反射 Reflect

Reflect 设计的目的
1. 将Object属于语言内部的方法放到Reflect身上
2. 修改某些Object方法的返回结果
3. 让Object操作变成函数行为
4. Reflect对象和Proxy的对象方法一一对应

本站文章如未注明均为原创 | 文章作者:刘晓帆 | 转载请注明来自:前端印象

发表评论

邮箱地址不会被公开。 必填项已用*标注

浏览量:307 次浏览