ES6(二)
ES5 - Object.defineProperty
简介:
ES5规范开始后续版本迭代,也在致力于做一件事,就是把js底层已有的功能,提供给开发者使用。Object.defineProperty 就是其中一个,此方法会可直接在一个对象上定义一个新的具有详细描述的属性,或者修改一个对象现有的属性,并返回这个对象
使用:
object.defineProperty(对象,属性,描述符):
描述符对,对象的属性进行详细描述:
数据描述符:
value: 'xxx' 属性值。默认''
writable: true 是否可写。默认false
configurable:true 是否可配置。默认false (可以通过delete 进行删除)
enumerable:true 是否可枚举。默认false ( 可以for in 循环 )
存取描述符:
set: function(){} 属性访问器 进行写操作时调用该方法
get:function(){} 属性访问器 进行读操作时调用该方法
var obj = {、 name: ‘ccc’ } // value ccc // writable obj.name = 'wew' // configurable delete obj.name console.log( delete obj.name ) // true // enumerable for ( var prop in obj ) { console.log(prop) }
// 不可写 writable false
Function.prototype = {
name: 'ccc'
}
// 不可配置的 通过var 定义的全局变量不能够删的
var a = 10
console.log( window.a )
// Object.prototype 的属性是不可被枚举的
var obj = {
name: 'ccc',
age: 12,
__proto__: {
sex: 'male'
}
}
for ( var prop in Object.prototype ) {
}
// 天生就有属性了,就可读可写 可配置 可枚举 var obj = { } var tempValuie = '' Object.dfineProperty( obj, 'name', { // value: 'ccc', ( value、writable 不能和get set 同时使用 ) // writable: false, (其他不写默认都是 false) configurable: true, enumerable: true, get: function () { //return 'ddd' return tempValue } set: function ( newValue ) { template = newValue } } ) console.log( obj.name ) // ddd '' obj.name = 10 console.log( obj.name ) // 10
var obj = {
// 其余默认, 比defineProperty 写起来方便
tempValue = '2323',
get name () {
return this.tempValue
},
set name (newValue) {
this.tempValue = newValue
}
}
注意:
如果描述符中同时出现, value、writable,和 set、get两组的话,会出现异常。切记不要同时使用
作用:
双向数据绑定的核心方法,主要做数据劫持操作(监控属性变化),同时是后期ES6中很多语法糖底层实现的核心方法
ES5 - 数据劫持
VUE 双向数据绑定核心功能由 Observer、Compile、Watcher 三部分实现其中 Observer 部分功能实现有 Object.definePropertry实现
Observer: 监测数据变化进行相应回调(数据劫持):
实现一个简单的数据劫持,作为Object.defineProperty 的练习。从而引出Proxy & Reflect
<input type="text" id="demo" /> <div id="show"></div>
var oDiv = document.getElementById('show') var oInput = document.getElementById('demo') var oData = { valueobj: { value: 'duyi', name: 'hha' }, value: 'ddd' , name: 'hah' } oInput.oninput = function () { oDate.value = this.value } function upDate () { oDiv.innerText = oData.value } upDate() // 监控对象的某个属性是否发生改变 functrion Observer (data) { if (!data || typeof data != 'object') { retrun data } //for ( var prop in data ) {} //object.keys( data ) // 返回的是 将对象中的属性放在数组中[ 'value' , 'name'] Object.keys(data).forEach( function (key) { definedReactive( data, key, data[key] ) } ) } function definedReactive ( data, key, val ) { Observer(val) // 属性值是对象的时候需要递归 Object.definedProperty( data, key, { get () { return val }, set ( newValue ) { if ( newValue == value ) return val = newValue unDate() } } ) } Observer(oData) // vue 处理数组 var arr = [] let { push } = Array.prototype function upData () { console.log('更新') } // [ 'push', 'pop' ] // arr push pop unshift slice... Object.defineProperty( Array.prototype, 'push', { value: (function () { return (...arg) => { push.apply( arr, arg ) upData() } })() } ) arr.push(1,2)
有缺点: 不能直接搞定数据
oData.aa = 10 新加的属性在Observe 后面不会更新,需要在新加属性后面 Observe
// Proxy Reflect 兼容性不好 vue 3。0
ES6变化 - Proxy & Reflect
let oData = { value: 'sss', _val: 'aaa' } let oProxyData = new Proxy( oData, { set ( target, key, value, receiver ) { Reflect.set( target, key, value ) upDate() }, get ( target, key, receiver ) { return Reflect.get( target, key ) }, has ( target, key ) { return key.indexOf( '_' ) != -1 false : key in target }, deleteProperty () { } } )
ES6 - Proxy & Reflect
简介:
植入代理模式的思想,以简洁易懂的方式控制对外部对象的访问
总结:
利用内置的 set、get 方法控制属性的读写功能用处较大
其余 has deleteProperty... 等方法不太在工作开发中使用
有兴趣可以去了解一下
兼容不好
let oData = { val: ‘saas’ } let oProxyData = new Proxy(oData, { set (target, key, value, receiver) { Reflect.set( target, key, value ) upDate() }, get ( target, key, receiver ) { // target {val:‘saas’} 返回对象 oData // key “val” 返回属性 // receiver Proxy {val: "saas"} 返回代理对象 new Proxy(...) // return target[key] return Reflect.get(target, key) }, has (targt, key) {
return key.indexOf('_') != -1 ? false : key in target
},
deleteProperty () {
}
}) // 读写 控制 console.log( oProxyData.val ) function upDate () { console.log(' 更新了 ') }
ES5 - Class (构造函数)回顾
面向对象思想简介:
面向过程目的在于把功能拆分成步骤,一环扣一环的完成,但是需求复杂到一定程度后,对开发者能力的挑战也是越来越强
面向对象目的在于前期把功能拆分并抽象成不同的对象,聚焦于每个对象的能力和他们之间的配合,项目复杂后相对于面向过程来讲较为轻松一些
举个例子:大象装冰箱
面向对象的编程语言需要具备 封装 继承 多态,js不是面向对象的语言而是基于对象的语言,js中基本上一切皆是对象
ES5 - Class 回顾
回顾ES5;
构造对象,私有属性、公有属性,私有属性继承,公有属性继承
// ES5 => ES6 // 公有属性(原型上) Plane.prototype.fly = function () { console.log( 'fly' ) } function Plane (name) { // 私有属性 this.name = "普通飞机"; this.blood = 100; this.fly = function () { console.log('fly') } } var oPlane = new Plane() // 继承 扩展功能 (装饰者模式) // 继承 继承原型 私有属性 //AttackPlane.prototype.__proto__ = Plane.prototype Object.setPrototypeOf(AttackPlane.prototype, Plane.prototype) AttackPlane.prototype.dan = function () { console.log('biubiu~') } function AttackPlane (name) { //this.name = '攻击机' //this.blood = 100 // 借用构造函数的方式 Plane.call( this, name ) }
ES6变化 - Class
核心变化
class、constructor、static、extends、super
// 私有属性 公有属性(原型属性) 静态属性(函数属性) class Plane { // ES7 静态属性 写法 // static alive = 10 static alive () { return true } // 私有属性 constructor (name) { this.name = name || '普通飞机' this.blood = 100 } // 原型方法 fly () { console.log( ‘fly’ ) } // ES7 私有属性 写法 // name = 10 } var oP= new Plane() Plane.alive() // true // ES6 继承方式 extends + super class AttackPlane extends Plane{ constructor (name) { super(name) // 相当于 Plane.call(this, name) this.logo = 'cc' } dan () { console.log('biubiu') } }
ES6 Class
1. 必须要new
2. 通过class 定义的类 Plane.prototype 和 static 不能枚举
3. 静态属性要放到 Plane 非原型
ES6 - 模拟实现Class
functuion _classCallCheck ( _this, _constructor ) { if ( !(_this instanceof _constructor) ) { throw "TypeError: Class constructor Plane cannot be invoked without 'new'" } // 暴露在全局下不太好 //function Plane () { //_classCallCheck( this, Plane ) // 把私有属性 公有属性 静态属性 //} // 处理公有属性和静态属性 // function _defineProperties ( target, prop ) { // Object.defineProperty props.forEach(function (ele) { // ele.key ele.value Object.defineProperty( target, ele.key, { value: ele.value, writable: true, configurable: true } ) }) } function _createClass ( _constructor, _prototypeProperties, _staticProperties ) { // 给原型上赋值 if (_prototypeProperties) { _defineProperties( _constructor.prototype, _prototypeProperties ) } if (_staticProperties) { _defineProperties( _constructor, _staticProperties ) } } var Plane = (function () { function Plane (name) { // 判断是否以new 的方式来执行 _classCallCheck( this, Plane ) this.name = name || '普通飞机' this.blood = 100 } _createClass( Plane, [ { key: 'fly', value: function () { console.log('fly') } } ], [ { key: 'alive', value: function () { return true } } ] ) return Plane })() // 实现继承 function _inherit ( sub, sup ) { Object.setPrototypeOf( sub.prototype, sup.prototype ) } var AttackPlane = ( function ( Plane ) { _inherit( AttackPlane, Plane ) function AttackPlane ( name ) { _classCallCheck( this, Plane ) var _this = this var that = Plane.call( _this, name ) if ( typeof that == 'object' ) { _this = that } _this.logo = 'ds' return _this } _createClass( AttackPlane, [ { key: 'dan', value: function () { console.log('biubiu') } } ], [ { key: 'alive', value: function () { return true } } ] ) return AttackPlane } )(Plane) new Plane()
ES7变化 - Class 提案属性
新特性:
static property = xxx 静态属性
property = xxx 私有属性
@decorator 装饰器
提案特性需要下载:
npm install @babel/plugin-proposal-decorators
需要配置:
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
calss Search { // ES7 static num = 10 // ES6 static num () { return 6 } constructor () { this.keyValue = ' ' } //ES7 私有属性 url = ‘urlA-’ @readOnly getCount () { console.log('发送请求') } }
ES7 - Decorator
// 张三 var oInput = document.getElementById('inp') var oBtn = document.getElementById('btn') var keyValue = '' oInput.oninput = function () { keyValue = this.value } oBtn.onclick = function () { newGetContent( keyValue ) } function getContent ( data ) { var url = 'url-A' console.log(url + ‘发送网络请求,数据:‘ + data) } var newGetContent = dealFunc( getCountent ) // 李四 function dealFunc ( func ) { retrun functuion (data) { // var urlB = 'urlB' consoloe.log(urlB + ‘发送网络请求,数据:‘+ data ) return func.apply( this, arguments ) } }
@Skin
class Search { constructor () { this.keyValue = '' } @myReadOnly url = 'urlA-' //@dealData @dealData('张三')
getContent () { console.log( this.url +'发送网络请求,数据:'+ this.keyValue ) } }
Decorators.js
// 装饰类
function Skin (target) {
target.aaa = 30 // Search.aaa 30
}
// 装饰私有属性 descript 没有value 只有 initializer, 原型的时候有 value 没initializer function myReadOnly ( proto, key, descriptor ) { console.log( proto, key, descriptor ) descriptor.writable = false console.log( descriptor.initializer() ) // urlA- descriptor.initializer = function () { return 6 // 6 改变了值 } } // 装饰原型上的属性 (箭头函数的时候是私有属性) function dealData ( proto, key, descriptor ) { console.log( proto, key, descriptor ) let oldValue = deescriptor.value // getContent() descriptor.value = function () { var urlB = 'urlB-' console.log(urlB + '发送网络请求,数据' + this.keyVal) console.log(0) oldValue.apply( this, arguments ) } }
function dealData (ms ) {
return function( proto, key, descriptor ) {
console.log( proto, key, descriptor )
let oldValue = deescriptor.value // getContent()
descriptor.value = function () {
var urlB = 'urlB-'
console.log(urlB + '发送网络请求,数据' + this.keyVal + ms)
console.log(0)
oldValue.apply( this, arguments )
}
}
}
let oS = new Search() oInput.oninput = function () { oS.keyValue = this.value } oBtn.onclick = function () { oS.getContent() }