JavaScript
物件屬性存取
物件屬性存取
const person = {};
// set
person.name = 'Bob';
// get
const name = person.name;
物件屬性存取
0. getter & setter
1. Object.defineProperty
2. Proxy & Reflect
3. Private Property
getter 跟 setter
let name;
const person = {
get name() {
return name;
},
set name(newName) {
name = newName;
},
}
person.name = 'Bob';
console.log(person.name); // Bob
ES5.1
getter 跟 setter
let name;
function getName() {
return name;
}
function setName(newName) {
name = newName;
}
const person = {}
person.__defineGetter__('name', getName);
person.__defineSetter__('name', setName);
person.name = 'Bob';
console.log(person.name); // Bob
ES5 以前
getter 跟 setter
屬性值 取出 跟 存入 的時候叫的 function
function doNothing() {}
const person = {
get name() {
return 'Stanney';
},
set name(newName) {
doNothing();
},
};
person.name = 'Bob';
console.log(person.name); // Stanney
Object.defineProperty
parameters:
(1) targetObject
(2) key (property name)
(3) descriptor
.get
.set
.value
.writable
.enumerable
.configurable
ES5.1
function doNothing() {}
const person = {};
Object.defineProperty(person, 'name', {
get() {
return 'Stanney';
},
set(newName) {
doNothing();
},
});
Object.defineProperty
parameters:
(1) targetObject
(2) key (property name)
(3) descriptor
.get
.set
.value
.writable
.enumerable
.configurable
ES5
const person = { name: 'Bob' };
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// {
// value: "Bob",
// writable: true,
// enumerable: true,
// configurable: true
// }
-> 能不能修改屬性值
-> 能不能用 Object.keys(target), for ... in
-> 能不能改屬性的 descriptor
const person = {};
Object.defineProperty(person, 'name', { value: 'Bob' });
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// {
// value: "Bob",
// writable: false,
// enumerable: false,
// configurable: false
// }
Object.defineProperty
getter, setter 無法被綁定到
同時擁有實際數值的屬性
const personA = { get name() { return 'Bob' } };
Object.getOwnPropertyDescriptor(personA, 'name');
// {
// get: ƒ,
// set: undefined,
// enumerable: true,
// configurable: true
// }
const personB = { name: 'Bob' };
Object.getOwnPropertyDescriptor(personB, 'name');
// {
// value: "Bob",
// writable: true,
// enumerable: true,
// configurable: true
// }
Proxy
const person = {};
const handler = {
get(target, prop) {
if (prop === 'name') return 'Bob';
return target[prop];
},
};
const personYouSee = new Proxy(person, handler);
console.log(personYouSee.name); // Bob
ES6
parameters:
(1) targetObject
(2) handler
Proxy
handler
.has()
.get()
.set()
.getPrototypeOf()
.setPrototypeOf()
.isExtensible()
.preventExtensions()
.getOwnPropertyDescriptor()
.defineProperty()
.deleteProperty()
.ownKeys()
.apply()
.construct()
the in operator
getting property values
setting property values
Object.getPrototypeOf
Object.setPrototypeOf
Object.isExtensible
Object.preventExtensions
Object.getOwnPropertyDescriptor
Object.defineProperty
the delete operator
Object.getOwnPropertyNames (Symbols)
function call
the new operator
Reflect
.has()
.get()
.set()
.getPrototypeOf()
.setPrototypeOf()
.isExtensible()
.preventExtensions()
.getOwnPropertyDescriptor()
.defineProperty()
.deleteProperty()
.ownKeys()
.apply()
.construct()
prop in target
target[prop]
target[prop] = newValue
Object.getPrototypeOf
Object.setPrototypeOf
Object.isExtensible
Object.preventExtensions
Object.getOwnPropertyDescriptor
Object.defineProperty
delete target[prop]
Object.getOwnPropertyNames (Symbols)
target[prop].apply()
new target()
和 proxy 可以代理的行為一對一對應
Reflect.has(person, 'name') =
'name' in person
Proxy & Reflect
const personYouSee = new Proxy(person, {
get(target, prop) {
if (prop === 'name') return 'Bob';
return Reflect.get(target, prop);
}
};
Proxy
Object.defineProperty
定義行為
定義屬性
Private Property
變數名稱前面加底線
const person = {
_name = 'Bob',
};
console.log(person._name); // Bob
假的私有變數
constructor 內定義 closure
function funcA() {
const scoped = 'secret';
function funcB() {
console.log(scoped);
}
return funcB;
}
const printSecret = funcA();
printSecret(); // secret
什麼是 closure
閉包(Closure)是函式以及該函式被宣告時所在的作用域環境(lexical environment)的組合。(MDN)
Operationally, a closure is a record storing a function[a] together with an environment. (Wikipedia)
constructor 內定義 closure
function Person() {
const name = 'Bob';
this.greet = function() {
console.log(`Hi, I'm ${name}.`);
};
}
const person = new Person();
person.greet(); // Hi, I'm Bob.
有用到私有變數的方法,不能定義在原型上
scoped WeakMap
什麼是 WeakMap?
ES6
1. collection of key/value pairs
2. key 一定要是 object,value 隨意
3. keys are weakly referenced
const person = {};
const weakMap = new WeakMap();
weakMap.set(person, 'someValue');
console.log(weakMap.get(person)); // someValue
weakMap.delete(person);
console.log(weakMap.has(person)); // false
scoped WeakMap
let person = {};
const weakMap = new WeakMap();
weakMap.set(person, 'someValue');
console.log(weakMap.get(person)); // someValue
person = null;
// 不需要先 weakMap.delete(person),
// 原來的 person/value pair 之後也會消失,
// 不會造成某種形式的 memory leak
什麼是 WeakMap?
keys are weakly referenced
scoped WeakMap
什麼是 WeakMap?
一個暫時放某個 object 的東西的地方
scoped WeakMap
const Person = (function () {
const privateProps = new WeakMap();
class Person {
constructor() {
privateProps.set(this, { name: 'Bob' });
}
greet() {
console.log(`Hi, I'm ${privateProps.get(this).name}.`);
}
}
return Person;
})();
const person = new Person();
person.greet(); // Hi, I'm Bob.
立即函式 (Immediately Invoked Function Expression)
scoped WeakMap 利用模組
const privateProps = new WeakMap();
class Person {
constructor() {
privateProps.set(this, { name: 'Bob' });
}
greet() {
console.log(`Hi, I'm ${privateProps.get(this).name}`);
}
}
export default Person;
scoped Symbol
const key = Symbol();
const object = {
[key]: '1234',
};
console.log(object[key]); // 1234
什麼是 Symbol?
ES6
1. primitive data type
2. every symbol value returned from Symbol() is unique
3. 可以作為 object 的 key
scoped Symbol
const Person = (function () {
const nameKey = Symbol();
class Person {
constructor() {
this[nameKey] = 'Bob';
}
greet() {
console.log(`Hi, I'm ${this[nameKey]}`);
}
}
return Person;
})();
const person = new Person();
person.greet(); // Bob
scoped Symbol 利用模組
const nameKey = Symbol();
class Person {
constructor() {
this[nameKey] = 'Bob';
}
greet() {
console.log(`Hi, I'm ${this[nameKey]}`);
}
}
export default Person;
const nameKey = Symbol();
class Person {
constructor() {
this[nameKey] = 'Bob';
}
greet() {
console.log(`Hi, I'm ${this[nameKey]}`);
}
}
const person = new Person();
console.log(Object.keys(person)); // []
console.log(Object.getOwnPropertyNames(person)); // []
const [sameNameKey] = Object.getOwnPropertySymbols(person);
scoped Symbol
Proxy
const instanceYouSee = new Proxy(instance, {
get: function(target, prop, value) {
if (prop.startsWith('_')) throw new Error();
return Reflect.get(target, prop);
},
set: function(target, prop, value) {
if (prop.startsWith('_')) throw new Error();
Reflect.set(target, prop, value);
},
has: function(target, prop) {
return prop.startsWith('_') ? false : Reflect.has(target, prop);
},
};
https://blog.techbridge.cc/2018/05/27/js-proxy-reflect/
Proxy
class Person {
constructor() {
this._name = 'Bob';
return new Proxy(this, {
get: function(target, prop, value) {
if (prop.startsWith('_')) throw new Error();
return Reflect.get(target, prop);
},
set: function(target, prop, value) {
if (prop.startsWith('_')) throw new Error();
Reflect.set(target, prop, value);
},
has: function(target, prop) {
return prop.startsWith('_') ? false : Reflect.has(target, prop);
},
});
}
}
https://blog.techbridge.cc/2018/05/27/js-proxy-reflect/
Thank you
Javascript 物件屬性
By luyunghsien
Javascript 物件屬性
- 490