对象、类、与面向对象编程
- 属性类型 -这些都是js内部引擎的规范定义不能改变
分为:数据属性和访问器属性
[[Configurable]] 是否可以通过delete删除 并重新定义,是否可以修改它的特性,以及是否可以把它改成访问器属性,默认 是true
[[Enumerable]] 表示属性是否可以通过for-in循环返回。直接定义在对象的属性的这个特性都是true
[[Writable]] 表示属性的值是否可以被修改,默认为true
[[Value]] 包含属性实际的值。读取和写入的位置,默认为undefined
/*要修改默认属性的默认特性
必须使用 Object.defineProperty() 这个方法 接收3个参数
要给其添加属性的对象
属性的名称
一个描述符对象(描述符对象上的属性包含) configurable\enumerable\writable\value
跟相关特性的名称一一对应。根据需要修改的特性,可以设置其中一个或多个值
*/
let person = {};
Object.defineProperty((person,"name",{
writable:false,
value:"Nicholas"
}));
console.log(person.name);
person.name = 'Greg';
console.log(preson.name)//Nicholas
delete person.name; //类似规则也适用于创建不可配置的属性
console.log(preson.name)//Nicholas
/*
访问器属性
访问器属性不包含数据值, 他们包含 gtter setter 函数,不够这两个函数不是必需的。
[[Configurable]] 表示属性是否可以通过delete删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性。默认这个属性为true
[[Enumerable]] 是否可以通过 for-in循环返回。 默认true
[[Get]] 获取函数,在读取属性时调用, 默认值为undefined
[[Set]] 设置函数,在写入属性时调用,默认undefined
*/
/*
访问器属性是不能直接定义的,必须使用Object.defineProperty()
*/
//定义一个对象,包含伪私有成员year_那公共成员edition
let book = {
year_:2017,
edition:1
};
Object.defineProperty(book,"year",{
get() {
return this.year_;
},
set(newValue) {
if(newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
})
book.year = 2018;
console.log(book.edition);// 2
//
/*
定义多个属性
Object.defineProperties()
这个方法可以通过多个描述符一次性定义多个属性
2个参数,(要为之添加或者修改属性的对象,另一个描述符对象,其属性与要添加和修改的属性一一对应。)
*/
let look = {};
Object.defineProperties(book,{
year_:{
value:2017
},
edition:{
value:1
},
year:{
get(){
return this.year_;
},
set(newValue) {
if(newValue >2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
}
})
/**
Object.getOwnPropertyDescriptor()
* 读取属性
* @param {obj} 对象 属性所在的对象
* @param {str} 字符串 要取得其描述符得属性名
* @param {obj} 返回是对象 根据属性类型不同 返回得 属性也不同
*/
let book = {};
Object.defineProperties(book,{
year_:{
value:2017
},
edition:{
value:1
},
year: {
get: function() {
return this.year_;
},
set: function(newValue){
if(newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
}
})
let descriptor = Object.getOwnPropertyDescriptor(book,"year_");
/*
*/
console.log(Object.getOwnPropertyDescriptors(book));
//es2017出的方法 会在每个自有属性上调用Object.getOwnPropertyDescriptor()并在一个新对象中返回它们
/**
* 合并
*
* assign()
* @param {obj} 返回是对象 一个或多个对象
*
* 先把每个原对象 Object.propertyIsEnumerable()返回true
* 和自有对象的Object.hasOwnProperty()返回true)属性复制到目标对象
* 使用原对象的get 目标对象的set
*
* 对对象操作是浅复制, 如果多个源对象都有相同的属性,则使用最后一个复制的值
*/
let dest,src,result;
dest = {id:'dest'};
result = Object.assign(dest,{id:'src1',a:'foo'},{id:'src2',b:'bar'});
//会覆盖之前相同的属性
console.log(reslut);//{ id: src2, a: foo, b: bar }
// 可以通过目标对象上的设置函数观察到覆盖的过程:
dest = {
set id(x) {
console.log(x);
}
};
Object.assign(dest, { id: 'first' }, { id: 'second' }, { id: 'third' });
// first
// second
// third
let dest ,src ,result;
/**
* 错误处理
*/
dest = {};
src = {
a: 'foo',
get b () {
//Object.assign()在调用这个获取函数时会抛出错误
throw new Error();
},
c:'bar'
};
try{
Object.assign(dest,src)
} catch(e) {
}
// Object.assign()没办法回滚已经完成的修改
// 因此在抛出错误之前,目标对象上已经完成的修改会继续存在:
console.log(dest); // { a: foo }
//对象的标识及相等判定
//这些是 === 符合预期的情况
console.log(true === 1); // false
console.log({} === {}); // false
console.log("2" === 2); // false
// 这些情况在不同JavaScript引擎中表现不同,但仍被认为相等
console.log(+0 === -0); // true
console.log(+0 === 0); // true
console.log(-0 === 0); // true
// 要确定NaN的相等性,必须使用极为讨厌的isNaN()
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true
//ES6 新增了 Object.is() 和===很像
/**
* 判断是否相等
*
* is()
* @param {obj} 传参2个对象
*
*/
console.log(Object.is(true, 1)); // false
console.log(Object.is({}, {})); // false
console.log(Object.is("2", 2)); // false
// 正确的0、-0、+0相等/不等判定
console.log(Object.is(+0, -0)); // false
console.log(Object.is(+0, 0)); // true
console.log(Object.is(-0, 0)); // false
// 正确的NaN相等判定
console.log(Object.is(NaN, NaN)); // true
//如果要检查多个值,递归地利用相等性传递即可
function recursivelyCheckEqual(x,...rest){
return Object.is(x,rest[0]) &&
(rest.lengt < 2 || recursivelyCheckEqual(...rest))
}
es6 属性值简写
let name = 'Matt';
let person = {
//name:name
name
};
es6 可计算属性
//old 之前想动态定义对象key值 需要这么做
const namekEY = 'name';
const ageKey = 'age';
let person = {};
person[nameKey] = 'Matt';
person[ageKey] = 27;
console.log(person) // {name:'Matt',age:27}
//new es6 现在动态定义可以这么写
let person = {
[nameKey]: 'Matt',
[ageKey]: 27
};
es6 简写方法名字
//old es5
let person = {
sayName: function() {
console.log('...')
}
}
person.sayName('Matt');//My name is Matt
//new es6
const methodKey = 'sayName';
let person = {
sayName(name) {
console.log('...')
}
[methodKey](name) {// 简写方法名与可计算属性健相互兼容
console.log('1212')
}
}
//配合计算属性本身可以是复杂的表达式
const nameKey = 'name';
const ageKey = 'age';
const jobKey = 'job';
let uniqueToken = 0;
function getUniqueKey(key) {
return `${key}_${uniqueToken++}`;
}
let person = {
[getUniqueKey(nameKey)]: 'Matt',
[getUniqueKey(ageKey)]: 27,
[getUniqueKey(jobKey)]: 'Software engineer'
};
console.log(person); // { name_0: 'Matt', age_1: 27, job_2: 'Software engineer' }
对象解构
1.解构 同名可以省略
2.解构可以结构对象属性和方法,把源数据结构转换为对象,但是null和undefined不能被解构
3.解构 可以同时进行定义默认值
4.解构 不要求变量必须在解构表达式种声明,如果是事先声明,解构表达式必须包含在一对括号中
5.嵌套解构 解构对于嵌套属性或赋值目标没有限制,可以通过复制对象属性(浅拷贝)
6.嵌套解构 可以使用嵌套结构进行解构
7.嵌套解构 外层属性没定义 源对象/目标对象都不能使用
8.对象解构 如果解构中途出错,只会部分解构
9.对象解构 在函数参数列表解构,不会对arguments有影响
let person = {
name:'Matt',
age:27
};
let {name, age} = person;
console.log(name); //Matt
console.log(age); //27
//可以在解构 时候进行 同时进行定义默认值
let person = {
name:'Matt',
age: 27
};
let {name,job='Software engineer'} = person;
console.log(name); //Matt
console.log(job); //Software engineer
创建对象的几种方法
工厂模式
- 大众化 应用于软件工程
function createPerson(name,age,job) {
let o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
console.log(this.name);
}
return o;
}
let person1 = createPerson('nicholas',29,"Software Engineer");
let person2 = createPerson('Greg',28,"Doctor");
function Person(name,age,job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name)
}
}
let person1 = new Person('Nicholas',12,"student")
let person2 = new Person('Jack',11,"police")
new 操作符都做了什么
(1) 在内存中创建一个新对象。
(2) 这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性。
(3) 构造函数内部的this被赋值为这个新对象(即this指向新对象)。
(4) 执行构造函数内部的代码(给新对象添加属性)。
(5) 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
这是我在开课吧学习记下来的笔记,开课吧非常好,大家在一起直播上课,课上老师幽默风趣,贼逗乐,讲的通俗易懂。很牛逼,爱了爱了,课后微信群里大家交流很积极,助教小姐姐很耐心的教,啊哈哈哈哈
下面是我在开课吧上课的截图