JavaScript04-对象02
1.工厂方法创建对象
function createObject(name, age, address) {
let obj = {};
obj.name = name;
obj.age = age;
obj.address = address;
return obj;
}
let a1 = createObject('bob', 10, 'A');
let a2 = createObject('tom', 20, 'B');
let a3 = createObject('alice', 30, 'B');
console.log(a1); // {name: 'bob', age: 10, address: 'A'}
console.log(a2); // {name: 'tom', age: 20, address: 'B'}
console.log(a3); // {name: 'alice', age: 30, address: 'B'}
2.构造函数
- 构造函数的定义。
// 1 使用构造函数也可以创建对象。
// 1) 构造函数专门用来创建对象。
// 2) 构造函数和普通函数的定义方式没有区别,唯一的区别就是构造函数的首字母需要大写。
// 3) 构造函数和普通函数的区别在于调用方式,
// 如果一个函数直接调用,则他是一个普通函数。
// 如果一个函数使用new调用,则他是一个构造函数。
function Person() {
this.name = 'tom';
this.age = 10;
}
// Person();表示是一个普通函数,Person()函数没有返回值,所以a1 = undefined。
let a1 = Person();
console.log(a1); // undefined
// 构造函数。
let a2 = new Person();
console.log(a2); // Person {name: 'tom', age: 10}
// 2 一个构造函数也成为一个类,通过该构造函数所创建的对象成为该类的实例。
// 可以使用 instanceof 来判断一个对象是否是一个类的实例。
console.log(a1 instanceof Person); // false
console.log(a2 instanceof Person); // true
- 构造函数的执行流程。
// 1 创建一个新对象。
// 2 将this赋值给新对象。
// 3 执行构造函数中的代码。
// 4 将对象返回。
- 构造函数中的方法。
// 1 将方法定义在构造函数中,构造函数每执行一次,就会创建出一个新的函数对象,
// 这些函数的功能都是一样的,会占用很多的内存空间。
function Person(name, age) {
this.name = name;
this.age = age;
this.run = function () {
console.log(`name is ${name}`);
}
}
let p1 = new Person('tom');
let p2 = new Person('alice');
// 每个构造函数执行时都会创建一个run()方法对象。
console.log(p1.run === p2.run); // false
// 2 可以通过将方法定义在全局作用域中来避免每次执行构造函数,都会创建出一个新的函数对象。
// 但是这种方式会污染全局的命名空间。
function run() {
console.log(`name is ${name}`);
}
function Student(name) {
this.name = name;
this.run = run;
}
let s1 = new Student('a01');
let s2 = new Student('a02');
console.log(s1.run === s2.run); // true
3.原型模式
// 1 每个函数对象中都有一个属性叫做prototype,这个属性指向一个对象,所指向的这个对象成为原型对象。
// 2 如果函数作用为普通函数调用,则原型对象(prototype)没有任何作用。
// 3 如果函数作用为构造函数调用,那么通过该构造函数所创建的对象中都有
// 一个隐含的属性(__proto__)指向函数的原型对象(prototype)。
function Person() {
}
// 访问普通函数的原型对象
console.log(Person.prototype); // {constructor: ƒ}
// 使用构造函数创建的对象中都有一个属性(__proto__)指向函数的原型对象(prototype)
let p1 = new Person();
let p2 = new Person();
console.log(p1.__proto__); // {constructor: ƒ}
console.log(p1.__proto__ === Person.prototype); // true
console.log(p1.__proto__ === p2.__proto__); // true
// 4 类的所有的实例中都有一个隐含属性指向构造函数的原型对象(prototype)。
// 5 原型对象(prototype)相当于一个公共的区域,可以被该类的所有实例访问。
// 6 可以将一些实例的公共属性和方法存储在原型对象中,这样只需要设置一次,类的所有实例都可以访问。
// 向Person类的原型对象中添加属性name
Person.prototype.name = 'tom';
// 原型中的属性和方法被类的所有实例共享。
console.log(p1.name); // tom
console.log(p2.name); // tom
// 7 当访问对象的属性时,JS会现在对象本身中寻找,如果对象本身中有这个属性,则直接返回属性的值。
// 如果对象本身没有这个属性,则会去对象的原型(__proto__)中寻找。
function Student() {
}
let s1 = new Student();
let s2 = new Student();
let s3 = new Student();
// 向Student的原型中添加属性name
Student.prototype.name = 'tom';
// 向Student的实例s1中添加name属性。
s1.name = 'alice';
// 对象本身有name属性,直接返回对象返回的name属性的值。
console.log(s1.name); // alice
// 对象本身没有name属性,则去原型中寻找name属性。
console.log(s2.name); // tom
console.log(s3.name); // tom
4.对象的属性检查
function Person(name) {
this.name = name;
}
Person.prototype.test = 'test';
let p1 = new Person('tom');
// in检查属性。如果属性存在于原型中,会返回true。
console.log('test' in p1); // true
// hasOwnProperty() 检查属性是否存在于对象本身。
console.log(p1.hasOwnProperty('test')); // false
// 检查对象是否存在于对象的原型中。
console.log(p1.__proto__.hasOwnProperty('test')); // true
5.原型链
- 原型链。
// 原型链。
// 1 当我们访问一个属性时,JS会现在对象本身中寻找;如果找到则使用,没有找到则在对象的原型中寻找;
// 如果找到则返回,没有找到则在原型的原型中寻找;如果找到则使用,没有找到以此类推;
// 直到找到Object的原型,如果依然没有找到则返回undefined,这个搜索过程成为原型链。
// Object的prototype是所有对象的原型,它的原型为null。
function Student(name) {
this.name = name;
}
Student.prototype.test = 'test';
let s1 = new Student();
console.log(s1.hasOwnProperty('hasOwnProperty')); // false
console.log(s1.__proto__.hasOwnProperty('hasOwnProperty')); // false
console.log(s1.__proto__.__proto__.hasOwnProperty('hasOwnProperty')); // true
// 检查对象的原型链上是否存在该类
console.log(s1 instanceof Object); // true
// s1.__proto__.__proto__就是Object的prototype,同时Object的prototype是null。
console.log(s1.__proto__.__proto__.__proto__); // null
// s1.__proto__.__proto__就是Object的prototype
console.log(s1.__proto__.__proto__ === Object.prototype); // true
- 获取原型链。
// 原型的获取。
// Object是一个构造函数,获取原型可以使用Object.prototype。
// Object.prototype是一个对象,获取原型需要使用Object.prototype.__proto__。
console.log(Object.prototype.__proto__); // null
let a = {};
// a.__proto__,隐式获取原型,一般不使用隐式原型(a.__proto__)来修改属性。
console.log(a.__proto__);
function MyClass() {
}
// MyClass.prototype,显示获取原型。
console.log(MyClass.prototype);
- 原型中静态方法和实例方法。
let a = {};
// 原型中静态方法和实例方法
// 实例方法,在Object原型中的都是实例方法,所有方法都可以直接访问。
// Object.prototype.hasOwnProperty(),就属于实例方法
console.log(Object.prototype.hasOwnProperty('test')); // false
// 都有的对象都可以直接方法实例方法。
console.log(a.hasOwnProperty('test')); // false
// 静态方法,可以直接通过构造函数Object来调用。
console.log(Object.assign('test')); // String {'test'}
// 如果使用对象来调用静态方法就会报错,Uncaught TypeError: a.assign is not a function
// console.log(a.assign('t')); // 报错
6.方法的重写
function User(name, age) {
this.name = name;
this.age = age;
}
let u1 = new User('tom', 10);
// 没有重写toString()方法。
// alert打印u1时,输出[object Object]
alert(u1); // [object Object]
// console.log()打印u1,输出 User {name: 'tom', age: 10}
console.log(u1); // User {name: 'tom', age: 10}
// console.log()打印u1.toString()输出 [object Object]
console.log(u1.toString()); // [object Object]
// 重写对象的toString()方法。
let u2 = new User('alice', 20);
u2.toString = function () {
return `${this.name} === ${this.age}`;
};
// 重写toString()之后的输出。
alert(u2); // alice === 20
console.log(u2); // User {name: 'alice', age: 20, toString: ƒ}
console.log(u2.toString()); // alice === 20
// 可以在对象的原型中重写toString(),修改对象的每个实例的toString()方法的输出。
Person.prototype.toString = function () {
return 'toString';
}
7.对象的分类
- 内建对象。由ES标准所规定的对象,Object Function String Boolean Number Array Math Date JSON。
- 宿主对象。由JS运行环境所提供的对象,Window Console Document。
- 自定义对象。由开发人员定义的对象。
8.连续赋值
let a = {x: 10};
let b = a;
console.log(b); // {x: 10}
a.x = a = {n: 20};
/*
a.x = a = {n: 20};
相当与
a.x = {n: 20}
a = {n: 20}
a.x = a = {n: 20};
会同时进行a.x和a的赋值,但是a.x中.的运算级别高,所以会先执行。
*/
// a.x为undefined,a = {n: 20}后执行。
console.log(a.x); // undefined
console.log(a); // {n: 20}
// a.x = {n: 20}相当与b.x = {n: 20},当时a被{n: 20}覆盖,b没有,
// 所以b.x = {n: 20}
console.log(b.x); // {n: 20}
console.log(b); // {x: {…}}
let m = n = 20;
/*
let m = n = 20;
相当与同时执行
let m = 20;
n = 20;
m使用let定义,所以不是window的属性,
n没有定义相当与var n = 20;所以是window的属性。
*/
console.log(window.m); // undefined
console.log(window.n); // 20