new操作符

let person = new Object();
person.name = "Nicholas";
person.age = 29;

对象字面量

对象字面量是对象定义的简写形式,目的是为了简化包含大量属性的对象的创建。

let person = { name: "Nicholas", age: 29
};

也可以使用字符串、数字、bool类型来定义。

let person = { "name": "Nicholas", "age": 29, 5: true,true: 123,false: 321
};
person[5];  //true
person[true];   //123
person[false];  //321

但要注意此类情况(后值覆盖前值):

let person = { "name": "Nicholas", "age": 29, 5: true,true: 123,true: 555,name: 'jack'
};
// 最后person变为
{5: true, name: "jack", age: 29, true: 555
}

访问/设置方式

点语法

let person = { "name": "Nicholas", "age": 29, 5: true,true: 123
};
person.name;    //"Nicholas"
person.age;     //29
person.name = 'jack';
person.age = 30;
person.true;    //对吗?
person.5;   //对吗?

中括号

let person = { "name": "Nicholas", "age": 29, 5: true,true: 123
};
person['name'];   //"Nicholas"
person['age'];        //29
person['name'] = 'jack';
person['age'] = 30;
person[true];   //对吗?
person[5];  //对吗?

configurable(可配置)

表示属性是否可以通过delete删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true

let person = {};
Object.defineProperty(person, "name", {configurable: false,value: undefined
});
console.log(person);    //{name: undefined}
delete person.name;  //false
console.log(person);    //{name: undefined}
let person = {};
Object.defineProperty(person, "name", {configurable: true,value: undefined
});
console.log(person);    //{name: undefined}
delete person.name;  //true
console.log(person);    //{}
let person = {};
Object.defineProperty(person,"name", {configurable: false,value: "Nicholas"
});
//Uncaught TypeError: Cannot redefine property: name
Object.defineProperty(person, "name", {configurable: true,value: "Nicholas"
});

enumerable

表示属性是否可以通过for-in循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是true

let person = {};
Object.defineProperties(person, {name: {enumerable: false},sex: {enumerable: true},age: {enumerable: true}
});
for (let key in person) {console.log(key);
}
// sex
// age

writable

表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是true

var obj = {};
Object.defineProperties(obj, {sex: {value: '男',writable: true},name: {value: '张三',writable: false}
});
obj.name = '李四';
obj.sex = '女';
console.log(obj);   //{sex: "女", name: "张三"}

value

包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为undefined

let person = {};
Object.defineProperty(person, "name", {value: "Nicholas"
});
console.log(person.name);   //Nicholas

get

获取函数,在读取属性时调用。默认值为undefined

let book = {year_: 2017, edition: 1
};
Object.defineProperty(book, "year", {get() {return this.year_;}
});
console.log(book.year); // 2017
console.log(book.edition); // 1

set

设置函数,在写入属性时调用。默认值为undefined

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.year); // 2018
console.log(book.edition); // 2

获取属性描述

getOwnPropertyDescriptor

获取指定属性的属性描述符。这个方法接收两个参数:属性所在的对象和要取得其描述符的属性名。返回值是一个对象,对于访问器属性包含属性的类型。

let book = { name: '张三' };
console.log(Object.getOwnPropertyDescriptor(book, 'name'));   //{value: "张三", writable: true, enumerable: true, configurable: true}

getOwnPropertyDescriptors

方法用来获取一个对象的所有自身属性的描述符。

let book = { name: '张三', age: 12 };
console.log(Object.getOwnPropertyDescriptors(book));
/**
{age: { value: 12, writable: true, enumerable: true, configurable: true }name: { value: "张三", writable: true, enumerable: true, configurable: true }
}
*/

合并对象

es6之前,通常会封装一个方法,可参考jqeuryextend方法。

var obj = {a: 1,b: 2
}
var obj1 = {c: 3,a: 5
}
{a: 5,b: 2,c: 3
}
//一个特别简单的浅拷贝
function extend(target, source) {target = target || {};if (typeof target !== 'object') {throw new Error('target不是对象');}if (!source) {throw new Error('source不能为空');}if (typeof source !== 'object') {throw new Error('source不是对象');}for (let key in source) {target[key] = source[key];  //然后将源对象的值赋值到target中}return target;  //最后返回target
}var obj = extend({a: 1,b: 2
}, {c: 3,a: 5
})
/**
{a: 5,b: 2,c: 3
}
*/

Object.assign

用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。源对象可以是多个,后者替换前者。

let obj = Object.assign({ a: 1 }, { b: 2 }, { c: 3 }, { a: 5 }, { b: 6 });
//{a: 5, b: 6, c: 3}

出现的问题:

以上方法都是浅拷贝,如果拷贝属性是对象,那么拷贝的只是引用。

var obj = {a: 1,b: 'fff'
}
var obj1 = {a: 2,c: {name: '李四',age: 20}
}
var obj2 = Object.assign({}, obj, obj1);
obj1.a = 3;
obj1.c.name = '王五';
obj1.c.age = 30;
console.log(obj2);  //obj2.c会跟着obj1.c的改变而改变

深拷贝

//深拷贝
function deepClone(target, source) {if (typeof target !== 'object' || !target) {return source || target;}if (typeof source !== 'object' || !source) {return source;}for (let key in source) {var item = source[key];if (typeof item === 'object' && item) {target[key] = deepClone(Array.isArray(item) ? [] : {}, item);} else {target[key] = item;}}return target;
}var obj = {a: 1,b: 'fff'
}
var obj1 = {a: 2,c: {name: '李四',age: 20}
}
var obj2 = deepClone(obj, obj1);
obj1.a = 3;
obj1.c.name = '王五';
obj1.c.age = 30;
console.log(obj2);  //obj2.c不会跟着obj1.c的改变而改变

解构

// 使用对象解构
let person = { name: 'Matt', age: 27 };
//可以使用别名 personAge
//相当于 let name = person.name;
//相当于 let personAge = person.age;
let { name, age: personAge } = person;
console.log(name, personAge);   //Matt   27
let person = { name: 'Matt', age: 27 };
let { job } = person;  //不存在的也可以解构,其实就相当于   let job = person.job;
console.log(job);   //undefined
let person = { name: 'Matt', age: 27, sex: null };
//也可以设置一个默认值,如果获取的值为undefined的话
//注:如果获取的值为null则不会取默认值,而是直接设置为null
let { name, job = 'Software engineer', sex = '男' } = person;
console.log(name); // Matt
console.log(sex);   //null
console.log(job); // Software engineer
//无法解构null和undefined
let { _ } = null; // TypeError
let { _ } = undefined; // TypeError
const [n1, n2, { a }] = [1, 2, { a: 3 }];   //解构数组
console.log(n1, n2, a); // 1 2 3
const [n1,...n2] = [1,2,3,4,5,6];
console.log(n1,n2);

创建对象

千万不要一个一个对象的去定义,不要重复相同的劳动,毫无意义!!!

简单工厂模式

简单理解工厂就是有一条标准化的流水线,只要输入参数就能按照标准流程生产出需要的产品。

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", 27, "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", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg
console.log(person1.constructor == Person); // true
console.log(person2.constructor == Person); // trueconsole.log(person2 instanceof Object); // true
console.log(person2 instanceof Person); // true
//写成函数表达式也行
let Person = function (name, age, job) {this.name = name;this.age = age;this.job = job;this.sayName = function () {console.log(this.name);};
}let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg
console.log(person1.constructor == Person); // true
console.log(person2.constructor == Person); // trueconsole.log(person2 instanceof Object); // true
console.log(person2 instanceof Person); // true

**缺陷:**每次sayName方法都要重新定义一次,其实这个方法只需定义一次即可。

原型模式

function Person() { }
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {console.log(this.name);
};

缺陷: 1. 原型上的对象数据会被共享,一个对象改了,其他对象也会跟着变。2. 创建对象不方便。

改良

function Person(name, age, job) {this.name = name;this.age = age;this.job = job;
}
//将无需重复定义/共享的定义在原型上
Person.prototype.sayName = function () {console.log(this.name);
};let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg

hasOwnProperty

hasOwnProperty()方法用于确定某个属性是在实例上还是在原型对象上。

function Person() { }
Person.prototype.age = 12;
let person1 = new Person;
person1.age = 20;
console.log(person1.age);   //20
console.log(person1.hasOwnProperty("age"));   //true  来自实例
delete person1.age;
console.log(person1.age);   //12
console.log(person1.hasOwnProperty("age"));   //false 来自原型

in

in操作符会在可以通过对象访问指定属性时返回true,无论该属性是在实例上还是在原型上。

function Person() { }
Person.prototype.age = 12;
let person1 = new Person;
person1.age = 20;
console.log(person1.age);   //20
console.log(person1.hasOwnProperty("age"));   //true  来自实例
'age' in person1; //true
delete person1.age;
console.log(person1.age);   //12
console.log(person1.hasOwnProperty("age"));   //false 来自原型
'age' in person1; //true

keys

Object.keys()方法接收一个对象作为参数,返回包含该对象所有可枚举的实例属性名称的字符串数组。

Object.keys({ a: 1, b: 2, c: 3 });  //["a", "b", "c"]

getOwnPropertyNames

Object.getOwnPropertyNames()方法可以获取所有实例属性,无论是否可以枚举。

function Person() {this.sex = '男';
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {console.log(this.name);
};
var p = new Person();
console.log(Object.getOwnPropertyNames(p)); //?

values

Object.values()返回对象值的数组。

const o = {foo: 'bar',baz: 1,qux: {}
};
console.log(Object.values(o)); // ["bar", 1, {}]

entries

Object.entries() 接收一个对象,返回键/值对的数组。

const o = {foo: 'bar',baz: 1,qux: {}
};
console.log(Object.entries(o)); // [["foo", "bar"], ["baz", 1], ["qux", {}]]

原型

每个对象都有一个特殊的属性叫作原型(prototype),在原型上定义的属性和方法会被每一个实例对象共享。

function Person() { }
Person.prototype.name = "无名";
Person.prototype.children = [];
Person.prototype.sayName = function () {console.log(`我的名字叫:${this.name}`);
}let zhangsan = new Person();
let lisi = new Person();
console.log(zhangsan.name);
console.log(lisi.name);
console.log(zhangsan.hasOwnProperty('name'));
zhangsan.name = '张三';
console.log(zhangsan.hasOwnProperty('name'));
console.log(lisi.name);
Person.prototype.name = '有名';
console.log(lisi.name);
console.log(zhangsan.name);
delete zhangsan.name;
console.log(zhangsan.name);lisi.name = '李四';
console.log(zhangsan.sayName());
console.log(lisi.sayName());
console.log(zhangsan.sayName === lisi.sayName);zhangsan.children.push('张欣');
console.log(zhangsan.children);
console.log(lisi.children);
console.log(zhangsan.hasOwnProperty('children'));
console.log(zhangsan.children === lisi.children);
//?

原型链

每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个内部指针指向另一个构造函数的原型对象,这样就在实例和原型之间构造了一条原型链。
过程:
先在对象本身查找–>构造函数中查找–>对象的原型中查找–>构造函数的原型中查找–>当前原型的原型中查找
理解:
原型链就是把原型串联起来,而原型链的最顶端为null,原型是来解决对象的共享属性或者共享方法对象具有_proto_原型而函数具有prototype原型

function Person() { }
Person.prototype.constructor === ?;
Person.prototype.constructor.prototype === ?;
Person.prototype.__proto__ === ?let zhangsan = new Person;
zhangsan.constructor === Person;
zhangsan.__proto__ === ?;
zhangsan.__proto__.__proto__ === ?
let obj = new Object();
obj.__proto__ === ?;
obj.__proto__.__proto__ === ?

面向过程

现在有个需求:创建一个人,张三、男、20岁。

let zhangsan = new Object;
zhangsan.name = '张三';
zhangsan.age = 20;
zhangsan.sex = '男';

又新来了一个需求:创建一个人,李四、男、30岁。

let lisi = new Object;
lisi.name = '李四';
lisi.age = 30;
lisi.sex = '男';

随着需求的不断增加,已经添加了1000个人。

let zhangsan = new Object;
zhangsan.name = '张三';
zhangsan.age = 20;
zhangsan.sex = '男';let lisi = new Object;
lisi.name = '李四';
lisi.age = 30;
lisi.sex = '男';
//...........剩下的3992行代码

然后突然有一天新增了一个需求:将所有年龄>=20的标记为青年,<20的标记为少年。

let zhangsan = new Object;
zhangsan.name = '张三';
zhangsan.age = 20;
zhangsan.sex = '男';
zhangsan.type = '青年';let lisi = new Object;
lisi.name = '李四';
lisi.age = 30;
lisi.sex = '男';
zhangsan.type = '青年';
//...........剩下的4990行代码

突然有一天,又要为每个人新增一个行为:能自我介绍。

…更多的需求

面向对象

将任何事物都想象成一个对象,并提炼出这个对象的属性、行为等,然后封装起来。

需求:创建一个人,张三、男、20岁。

此刻,首先需将人想象为一个对象,目前给出的三个属性是姓名、性别、年龄,将封装思想用起来。

function Person(name, age, sex) {this.name = name;this.age = age;this.sex = sex;
}let zhangsan = new Person('张三', 20, '男');

又新来了一个需求:创建一个人,李四、男、30岁。

let lisi = new Person('李四', 30, '男');

随着需求的不断增加,已经添加了1000个人。

function Person(name, age, sex) {this.name = name;this.age = age;this.sex = sex;
}let zhangsan = new Person('张三', 20, '男');
let lisi = new Person('李四', 30, '男');
//...   剩下的998行代码

然后突然有一天新增了一个需求:将所有年龄>=20的标记为青年,<20的标记为少年。

function Person(name, age, sex) {this.name = name;this.age = age;this.sex = sex;//只需在构造函数里新增一个判断即可,其他每个实例都不用动if (this.age >= 20) {this.type = '青年';}else {this.type = '少年';}
}

突然有一天,又要为每个人新增一个行为:能自我介绍。

function Person(name, age, sex) {this.name = name;this.age = age;this.sex = sex;if (this.age >= 20) {this.type = '青年';}else {this.type = '少年';}
}Person.prototype.say = function () {console.log(`我是${this.name},我今年${this.age}岁了,我是个${this.sex}人,算是${this.type}了`);
}let zhangsan = new Person('张三', 20, '男');
let lisi = new Person('李四', 30, '男');
//...  剩下的998行代码
console.log(zhangsan.say());
console.log(lisi.say());

继承

继承就是子继承父的所有属性和方法,如:父亲的基因被儿子继承,所以通过DNA(原型链)可追溯某人的向上继承关系。从技术上来讲就是继承父对象的所有属性和方法。

function Father() {this.money = 10000000000;   //百亿家产this.houses = 10;   //10栋别墅this.cars = 100;    //100辆车
}function Son() {Father.call(this);  //继承父亲的家产
}let son = new Son;
console.log(son.money, son.houses, son.cars);   //10000000000 10 100

原型链继承

function superType() {this.name = '张三';this.children = ['张欣', '张宇'];
}
superType.prototype.age = 12;
function sub() {}
sub.prototype = new superType();
var s = new sub();
console.log(s.name, s.age);    //张三 12
console.log(s.hasOwnProperty('name'));  //false
console.log(s.hasOwnProperty('children'));  //falsevar s1 = new sub();
s1.children.push('李四');
console.log(s1.children);
console.log(s.children);console.log(s.constructor === superType);   //true
console.log(s instanceof sub);  //true
console.log(s instanceof superType);    //true

盗用构造函数

function superType() {this.name = '张三';this.children = ['张欣', '张宇'];
}
superType.prototype.age = 12;
function sub() {superType.call(this);
}var s = new sub();    //从sub派生出来的,继承自sub
console.log(s.name);    //张三
console.log(s.hasOwnProperty('name'));  //true
console.log(s.hasOwnProperty('children'));  //truevar s1 = new sub();
s1.children.push('李四');
console.log(s1.children);
console.log(s.children);console.log(s.constructor === sub);   //true
console.log(s instanceof sub);  //true
console.log(s instanceof superType);    //false
//目前为止一切看起来都正常了
console.log(s.age, s1.age);

组合继承

组合继承(有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

function superType() {console.log('superType执行了一次');this.name = '张三';this.children = ['张欣', '张宇'];
}
superType.prototype.age = 12;
function sub() {superType.call(this);
}//直接继承原型,不用再次实例化父类构造函数,防止再次执行父类构造函数
//sub.prototype = Object.create(superType.prototype, { constructor:{ value:sub, enumerable:false } });//跟上面的一样,只是最后自己手动修改一下构造函数的指向
sub.prototype = Object.create(superType.prototype)//sub.prototype = new superType()
sub.prototype.constructor = sub;//使用浅拷贝的方式将自身的构造函数替换掉父类原型的构造函数
//sub.prototype = Object.assign(Object.create(superType.prototype), { constructor: sub });var s = new sub();
console.log(s.name);    //张三
console.log(s.hasOwnProperty('name'));  //true
console.log(s.hasOwnProperty('children'));  //truevar s1 = new sub();
s1.children.push('李四');
console.log(s1.children);
console.log(s.children);console.log(s.constructor === sub);   //true
console.log(s instanceof sub);  //true
console.log(s instanceof superType);    //true
console.log(s.age, s1.age);     //12 12

class类

与函数类型相似,定义类也有两种主要方式:类声明和类表达式。这两种方式都使用class关键字加大括号。类其实就是语法糖。

// 类声明
class Person {}
// 类表达式
const Animal = class {};

函数可以提升,但是类不能提升。

console.log(ClassDeclaration); //ReferenceError: ClassDeclaration is not defined
class ClassDeclaration {}//同样也受到块级作用域的限制
{ function FunctionDeclaration() {} class ClassDeclaration {}
}
console.log(FunctionDeclaration);   //FunctionDeclaration() {}
console.log(ClassDeclaration);  //ReferenceError: ClassDeclaration is not defined

类的构成

类可以包含构造函数方法、实例方法、获取函数、设置函数和静态类方法,但这些都不是必需的。空的类定义照样有效。默认情况下,类定义中的代码都在严格模式下执行

// 空类定义,有效
class Foo {}
// 有构造函数的类,有效
class Bar { constructor() {}
}
// 有获取函数的类,有效
class Baz { get myBaz() {}
}
// 有静态方法的类,有效
class Qux { static myQux() {}
}
class Person {constructor() {}name = '张三';_age = 17;children = [];say() {console.log(`我的名字叫:${this.name}`);}static isPerson(person) {return person instanceof Person;}get type() {if (this._age > 18) {return '青年';}return '少年';}get age() {return this._age;}set age(value) {if (value > 0 && value < 120) {this._age = value;}}
}let person = new Person();
console.log(person.age, person.type);
person.age = 0;
console.log(person.age);
person.age = 20;
console.log(person.type);
console.log(Person.isPerson(person), Person.isPerson(new Object()));
console.log(person.hasOwnProperty('name'), person.hasOwnProperty('children'));

类构造函数

constructor关键字用于在类定义块内部创建类的构造函数。方法名constructor会告诉解释器在使用new操作符创建类的新实例时,应该调用这个函数。

class Animal { }
class Person {constructor() {console.log('person ctor');}
}
class Vegetable {constructor(color) {this.color = color;}
}
let a = new Animal();
let p = new Person(); // person ctor
let v = new Vegetable('orange');
console.log(v.color); // orange
//和下面代码等价
function Person() {console.log('person ctor');
}
function Vegetable(color) {this.color = color;
}
let p = new Person(); // person ctor
let v = new Vegetable('orange');
console.log(v.color); // orange

super

super表示父对象。

class Father {constructor(name, age, sex) {this.name = name;this.age = age;this.sex = sex;}money = 0;    //总钱数//挣钱makeMoney(money) {this.money += money;console.log(`这次赚了${money},到目前为止总共已经赚了${this.money}了。`);}say() {console.log(`我叫${this.name},今年${this.age}岁。`);}
}class Son extends Father {constructor(name, age, sex) {super(name, age, sex);  //super作为父类构造函数只能用在子类构造函数中}say() {//super();    //报错super.say();console.log('我是我父亲的儿子,还在读大学');}
}let son = new Son('张三', 20, '男');
son.makeMoney(1000);
son.makeMoney(100);
son.say();
//人(基类)基本类
class Person {constructor(name, age, sex, skin, country) {this.name = name;this.age = age;this.sex = sex;this.skin = skin;this.country = country;}//定义的函数(自我介绍)introduction() {console.log(`我叫${this.name},今年${this.age}岁了,性别:${this.sex},国籍:${this.country},肤色:${this.skin}`);}
}//黄种人
class YellowPerson extends Person {constructor(name, age, sex, country) {super(name, age, sex, '黄色', country);}
}//中国人
class ChinesePerson extends YellowPerson {constructor(name, age, sex) {super(name, age, sex, '中国');}//自我介绍introduction() {super.introduction();console.log(`我们都会功夫,我们是世界第一!`);}
}let yellowPerson = new YellowPerson('张三', 20, '男', '新加坡');
let chinesePerson = new ChinesePerson('李四', 30, '男');
yellowPerson.introduction();
chinesePerson.introduction();

继承

ES6类支持单继承。使用extends关键字,就可以继承任何拥有[[Construct]]和原型的对象。很大程度上,这意味着不仅可以继承一个类,也可以继承普通的构造函数(保持向后兼容)。

class Father {money = 10000000000;   //百亿家产houses = 10;   //10栋别墅cars = 100;    //100辆车makeMoney(money) {console.log(`又赚了${money}元`);this.money += money;}
}class Son extends Father { }let son = new Son();
console.log(son.money, son.houses, son.cars);
son.makeMoney(10000);
console.log(son.money, son.houses, son.cars);
console.log(son.hasOwnProperty('money'), son.hasOwnProperty('houses'), son.hasOwnProperty('cars'));
console.log(son instanceof Son);
console.log(son instanceof Father);

单继承

一次只能继承一个父类,就好比一个人只能有一个父亲。但是可以嵌套继承,就好比儿子继承父亲,父亲继承爷爷。

//爷爷
class GrandPa { }
//父亲
class Father extends GrandPa{ }class Son extends Father, GrandPa { }    //错误
class Son extends Father extends Object { } //错误
class Son extends Father, extends Object { } //错误
class Son extends Father { }    //正确,同时拥有了爷爷和父亲的属性
//爷爷
class GrandPa {house = 10;
}
//父亲
class Father extends GrandPa {cars = 10;tellMe() {console.log(`我继承了我父亲的${this.house}套房子`);}
}
//儿子
class Son extends Father {tellMe() {console.log(`我不用劳动就继承了我爷爷的${this.house}套房子和我父亲的${this.cars}辆车子`);}
}var father = new Father;
var son = new Son;
father.tellMe();
son.tellMe();

**思考:**可以无限继承吗?

**练习:**用继承实现dialogalertconfirm等效果。

function Dialog(msg) {if (!(this instanceof Dialog)) {return new Dialog(msg);}this.id = Dialog.id++;this.Dialogs.push(this);this.init(msg);
}//所有的弹出框对象
Dialog.prototype.Dialogs = [];//计算底部位置
Dialog.prototype.getHeight = function () {return this.container.clientHeight;
}//使用观察者模式告知删除
//接收通知
Dialog.prototype.receive = function (height) {this.resetTop(height);
}//通知
Dialog.prototype.notify = function () {const height = -(this.getHeight() + 10);this.Dialogs.forEach(item => {if (item.id > this.id) {item.receive(height);}});
}//计算所有容器的高度总和
Dialog.prototype.calcAllHeight = function () {let height = 0;this.Dialogs.forEach(item => {if (item.id !== this.id) {height += item.getHeight();}});return height;
}Dialog.prototype.resetTop = function (top) {if (!isNaN(top)) {const originHeight = parseInt(this.container.style.top.replace('px'));  //原始高度top += originHeight;} else {top = this.calcAllHeight() + 10 * this.Dialogs.length;}//改变容器位置this.container.style.top = top + 'px'
}Dialog.prototype.init = function (content) {this.container = document.createElement('div'); //创建一个div容器this.container.style = `background: yellow;padding: 10px; border: #000000 1px solid; position: fixed;z-index: 99999;left: 43%;`;this.header = document.createElement('div');    //头部容器this.header.style = 'text-align:right;font-size:15px;';this.closeButton = document.createElement('label'); //关闭按钮this.closeButton.innerText = 'X';this.closeButton.style = 'font-weight:bold;';this.closeButton.onclick = () => {this.remove();};this.header.appendChild(this.closeButton);this.content = document.createElement('div');  //创建一个div用于显示content内容this.content.innerHTML = content;this.resetTop();this.container.appendChild(this.header);this.container.appendChild(this.content);document.body.appendChild(this.container);  //将容器加入到body最后
}Dialog.prototype.remove = function () {const index = this.Dialogs.findIndex(c => c.id === this.id);this.Dialogs.splice(index, 1);this.notify();  //通知下边的提示框已删除this.container.parentElement.removeChild(this.container);   //移除弹出框
}Dialog.id = 0;let d1 = new Dialog('这是第一个弹出框');
let d2 = Dialog('这是第二个弹出框');
let d5 = Dialog(`<table border="1">
<tr><td>姓名</td><td>年龄</td>
</tr>
<tr><td>张三</td><td>20</td>
</tr>
<tr><td>李四</td><td>30</td>
</tr>
</table>`);//弹出提示框
function MyAlert(msg, type) {Dialog.call(this, msg);     //将Dialog的this指向到当前实例对象上,所以使用this相当于给当前实例对象定义属性this.setIcon(type);
}//提示框继承Dialog,在Dialog的原基础上新增或修改
MyAlert.prototype = Object.create(Dialog.prototype);
MyAlert.prototype.constructor = MyAlert;MyAlert.prototype.setIcon = function (type) {this.icon = document.createElement('label');this.icon.innerText = type === 'fail' ? '×' : '√';this.icon.style = 'color:red;font-size:20px';this.container.insertBefore(this.icon, this.content);   //将图标加入到内容的前面this.okButton = document.createElement('input');this.okButton.type = 'button';this.okButton.value = '确定';//把内容容器改为inline-blockthis.content.style.display = 'inline-block';//将头部隐藏,因为不需要关闭按钮了this.header.style.display = 'none';//改变容器位置this.resetTop();//点击确定按钮时隐藏this.okButton.onclick = () => {this.remove();}this.buttonsContainer = document.createElement('div');  //用于存放按钮的容器this.buttonsContainer.appendChild(this.okButton);this.buttonsContainer.style = 'text-align:right;';this.container.appendChild(this.buttonsContainer);
}let d3 = new MyAlert('这是第一个失败的弹出框', 'fail');
let d4 = new MyAlert('这是第一个成功弹出框', 'success');//询问对话框
function MyConfirm(msg, title) {MyAlert.call(this, msg);this.setTitle(title);
}//询问对话框可以继承提示框,在提示框的原基础上新增或修改
MyConfirm.prototype = Object.create(MyAlert.prototype);
MyConfirm.prototype.constructor = MyAlert;//设置标题
MyConfirm.prototype.setTitle = function (title = '提示') {this.title = document.createElement('div');this.title.innerText = title;this.title.style = 'font-size:15px;font-weight:bold;';this.container.insertBefore(this.title, this.icon);//改变一下icon的内容this.icon.innerText = '?';//改变容器位置this.resetTop();this.cancelButton = document.createElement('input');this.cancelButton.type = 'button';this.cancelButton.value = '取消';this.cancelButton.onclick = () => {console.log('取消');this.remove();};this.buttonsContainer.appendChild(this.cancelButton);
}let c1 = new MyConfirm('你确定要删除吗?');
let c2 = new MyConfirm('你确定要删除吗?', '请看清楚');

js中对象属性、面向对象、面向过程、类、继承、以及原型原型链相关推荐

  1. js 中对象--属性相关操作

    查询属性: 可以用 对象.属性 来查询属性和属性方法               或者                    对象["属性"]  来查询属性和属性方法 演示代码: ...

  2. JS获取对象属性的各种方式和区别(自身/原型属性、可枚举/不可枚举)

    对象的属性有自身属性和原型属性之分,自身属性是对象自己的属性,原型属性是存在于原型链上的属性. 可以用Object.prototype.hasOwnProperty()判断是自身属性还是原型属性.(i ...

  3. js 中对象属性的特性

    数据属性: 数据属性包含一个数据值的位置,在这个位置可以读取和写入值. 4个描述的行为特性: writable  表示能否修改属性的值.默认为true Enumerable 表示能否过过for in循 ...

  4. js delete删除对象属性,delete删除不了变量及原型链中的变量

    js delete删除对象属性,delete删除不了变量及原型链中的变量 一.delete删除对象属性 function fun(){this.name = 'gg';}var obj = new f ...

  5. JS中对象按属性排序(冒泡排序)

    原文地址 https://www.cnblogs.com/it-Ren/p/10898947.html 一路向北√ 越努力,越幸运. JS中对象按属性排序(冒泡排序) 冒泡排序:它重复地走访过要排序的 ...

  6. JS 中对象的深浅拷贝(ES3、ES5、ES6不同方法底层实现,一文搞清楚深浅拷贝面试常问题)

    JS 中对象的深浅拷贝   拷贝我们都知道这个词的意思,我们经常做过复制.粘贴的操作,其中的复制就是拷贝,那么在拷贝的时候,如果我们复制出来的内容和原内容是完全的分开,各自不相影响,那么这就属于深拷贝 ...

  7. js中对象数组根据对象id分组并转map

    js中对象数组根据对象id分组并转map 如果要将具有相同 id 属性的对象元素,分成不同的数组. 可以先从对象数组中提取相同的 id 属性,再使用 Array.reduce() 和 Map 来进行对 ...

  8. 关于JS中target属性

    关于JS中target属性的使用 首先先介绍一下target这个属性--触发事件的元素 定义和语法 //定义 target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素 获取当前 ...

  9. 解决vue中对象属性改变视图不更新的问题

    解决vue中对象属性改变视图不更新的问题 参考文章: (1)解决vue中对象属性改变视图不更新的问题 (2)https://www.cnblogs.com/buxiugangzi/p/12050165 ...

  10. java合并后求和_Java8使用stream实现list中对象属性的合并(去重并求和)

    前言 需要对一个list中的对象进行唯一值属性去重,属性求和,对象假设为billsnums,有id.nums.sums三个属性,其中id表示唯一值,需要nums与sums进行求和,并最后保持一份. 例 ...

最新文章

  1. linux下的包和RPM管理
  2. 用Word 2010发布博文
  3. VMware-workstation-6.0 安装系统前必须映射光驱盘符
  4. 我们来聊点成年人的话题!
  5. bzoj 1042 HAOI2008 硬币购物
  6. c#语言程序设计上机实验,《C#语言程序设计》实 验 报 告
  7. 浏览器怪异模式和标准模式之间的区别 DTD
  8. Game HDU - 5242 树链思想
  9. ASP.NET页面输出缓存知识
  10. html简易幻灯片,用html5实现的简单幻灯片实例
  11. 王庆的边缘计算(第三章)
  12. testNg官方文档
  13. Nginx 漏洞扫描及修复方案
  14. 服务器运行一天死机,服务器死机怎么办?教你排除故障
  15. java计算机毕业设计售楼系统源码+mysql数据库+系统+lw文档+部署
  16. maven创建web项目
  17. 汇编:裴波那契数列前50项
  18. Excel2007版的常用功能(17):Excel数学函数
  19. PHP json_encode float
  20. python - pandas 之 dataframe - 行列筛选/遍历/新增/删除/连接/合并/修改/跨表update

热门文章

  1. linux编译firefox,linux安装firefox
  2. 前端+后端项目 - 论坛信息管理系统(Web+servlet+MySQL+JDBC)
  3. CentOS利用WebHook实现PHP自动部署Git代码
  4. CTGU 2021春-MySQL数据库实验2:基本查询3-4关,共7小题全代码+信息表+通关截图!
  5. CTGU实验6_2-创建借书存储过程
  6. WebMvcConfigurerAdapter已被废弃的解决方法
  7. 学习python的第二周 第一天
  8. 非负数正则表达式 js jquery demo
  9. 给IT新人的15个建议:苦逼程序员的辛酸反省与总结!
  10. 读博后降维打击数学建模!