一,JavaScript中对象的理解与创建方法
对象将相关联的数据封装在一起,是对一个事物的抽象概念。比如说把一个人当然对象,这个人可以有姓名、年龄、身高、体重等属性,可以进行工作,吃饭,看电视等动作。因此可以把这个对象描述为:
const person = {
name: "xiaoming",
age: 18,
height: 190,
eating: function () {},
working: function () {},
};JavaScript中创建对象除了用上述的字面量方法(常用)还可以使用object构造函数来创建。
const person = new Object();
person.name = "xiaoming";
person.age = 18;
person.height = 190;
person.eating = function () {};
person.working = function () {};二,对对象属性的控制——属性描述符
对象创建出来了,那如何对对象中的一些属性进行控制呢?比如说可不可以修改,可不可以遍历等。这就需要用到属性描述符。 属性描述符可以分为数据属性描述符和存取属性描述符。我们可以通过getOwnPropertyDescriptors或者getOwnPropertyDescriptor方法获取属性描述符。
let obj = {
name: "sun",
};
Object.defineProperty(obj, "height", {
value: 180,
});
console.log(Object.getOwnPropertyDescriptors(obj));
// {
// name: {
// value: "sun",
// writable: true,
// enumerable: true,
// configurable: true
// },
// height: {
// value: 180,
// writable: false,
// enumerable: false,
// configurable: false
// }
//这里获取的是对象属性的数据属性描述符。[[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符;
当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]为true;
当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false;
[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;
当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]为true;
当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false;
[[Writable]]:表示是否可以修改属性的值;
当我们直接在一个对象上定义某个属性时,这个属性的[[Writable]]为true;
当我们通过属性描述符定义一个属性时,这个属性的[[Writable]]默认为false;
[[value]]:属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改;
默认情况下这个值是undefined;
let obj = {
name: "sun",
};
Object.defineProperty(obj, "name", {
enumerable: true,
configurable: true,
get: function () {
console.log("获取name属性值");
},
set: function () {
console.log("设置name属性值");
},
});
console.log(Object.getOwnPropertyDescriptors(obj));
// {
// name: {
// get: [Function: get],
// set: [Function: set],
// enumerable: true,
// configurable: true
// }
// }这是获取的对象属性的存取属性描述符。enumerable和configurable一致,但是没有value和writable,多了set和get[[get]]:获取属性时会执行的函数。默认为undefined[[set]]:设置属性时会执行的函数。默认为undefined
此外,我们还可以通过Object.defineProperties() 方法直接在一个对象上定义 多个 新的属性或修改现有属性,并且返回该对象
let obj = {};
let obj1 = Object.defineProperties(obj, {
name: {
writable: true,
value: "sun",
},
age: {
value: 19,
},
});
console.log(Object.getOwnPropertyDescriptors(obj1));
// {
// name: {
// value: "sun",
// writable: true,
// enumerable: false,
// configurable: false
// },
// age: {
// value: 19,
// writable: false,
// enumerable: false,
// configurable: false
// }
// }三,创建多个对象的方案
上述介绍的两种创建对象的方法需要写大量重复的代码。如果需要创建多个对象,可以使用以下方法。
1,工厂模式
function CreatePerson(name, age, height) {
let obj = new Object();
obj.name = name;
obj.age = age;
obj.height = height;
return obj;
}
let xiaoming = CreatePerson("xiaoming", 12, 189);
let xiaohong = CreatePerson("xiaohong", 13, 179);
let xiaolv = CreatePerson("xiaolv", 14, 149);这样通过调用一次方法就返回一个包含多个公有属性的对象。但是酱子作会使创建出来的对象都是一样的类型。
2,构造函数模式
function Person(name, age, height) {
this.name = name;
this.age = age;
this.height = height;
this.eating = function () {
console.log(this.name + "在吃东西~");
};
this.running = function () {
console.log(this.name + "在跑步~");
};
}
let xiaoming = new Person("xiaoming", 12, 189);
let xiaohong = new Person("xiaohong", 13, 179);
let xiaolv = new Person("xiaolv", 14, 149);这样的话如果需要创建多个汽车对象的话,就可以通过再定义一个Car构造函数然后new Car来实现。啥是构造函数?JavaScript中的构造函数也是一个普通的函数,从表现形式来说,和千千万万个普通的函数没有任何区别;那么如果这么一个普通的函数被使用new操作符来调用了,那么这个函数就称之为是一个构造函数;new是啥?一个创建对象的操作符。 一个函数被new调用了它会执行以下操作:
在内存中创建一个新的对象(空对象)
这个对象的内部的proto属性会被赋值为这个构造函数的prototype属性
构造函数内部的this会被指向创建出来的新对象
执行构造函数内部的方法体
返回创建出来的新对象
通过构造函数创建多个对象就没有缺点了么?有,它在于我们需要为每个对象的函数去创建一个函数对象实例,比如说小明有eating方法而小红有running方法但是这样创建的话会使小明不仅有eating还有running。并且小明的是小明的,小红的是小红的,没有实现共用。
3,构造函数和原型相组合方式:将多个对象中相同的方法放到构造函数的原型上去。
function Person(name, age, height, address) {
this.name = name;
this.age = age;
this.height = height;
this.address = address;
}
Person.prototype.eating = function () {
console.log(this.name + "在吃东西~");
};
Person.prototype.running = function () {
console.log(this.name + "在跑步~");
};
var p1 = new Person("why", 18, 1.88, "广州市");
var p2 = new Person("kobe", 30, 1.98, "北京市");
p1.eating();
p2.running();四,对象的原型
JavaScript中每一个对象都有一个prototype属性,该属性指向另一个对象,这个属性有啥用呢?又如何获取呢?作用
当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作;
这个操作会首先检查该属性是否有对应的属性,如果有的话就使用它;
如果对象中没有改属性,那么会访问对象[[prototype]]内置属性指向的对象上的属性;
获取方式
通过对象的proto属性获取
通过Object.getPrototypeOf()方法获取
对象有proto属性,函数有prototype属性
let obj = {
name: "sun",
};
function person() {}
console.log(obj.__proto__); // [Object: null prototype] {}
console.log(person.prototype); // {}创建对象的内存表现

constructor属性默认情况下原型上都会添加一个属性叫做constructor,这个constructor指向当前的函数对象;
五,原型链
从一个对象上获取属性,如果在当前对象中没有获取到就会去它的原型上面获取
let obj = { name: "why", age: 18 };
obj.__proto__ = {};
obj.__proto__.__proto__ = {};
obj.__proto__.__proto__.__proto__ = { address: "北京市" };
console.log(obj.address);//北京市那么什么地方是原型链的尽头呢?
let obj = { name: "why", age: 18 };
console.log(obj.__proto__);//[Object: null prototype] {}[Object: null prototype] {} 即顶层原型,该对象有原型属性,但是它的原型属性已经指向的是null,并且该对象上有很多默认的属性和方法。
六,继承
JavaScript中可以通过原型链来实现继承
//1.定义父类构造函数
function Person() {
this.name = "sun";
}
//2.父类原型上添加内容
Person.prototype.running = function () {
console.log(this.name + "running~");
};
//3.定义子类构造函数
function Student() {
this.sno = 111;
}
// 4.创建父类对象,并且作为子类的原型对象
var p = new Person();
Student.prototype = p;
// 5.在子类原型上添加内容
Student.prototype.studying = function () {
console.log(this.name + "studying");
};
var s1 = new Student();//通过子类来调用父类中的方法
s1.studying();//"sunstudying"
s1.running(); //"sunrunning~"这里Person中有running方法,Student中有studying方法,通过让Student的原型指向person实现继承,下面打印说明了stu中确实有来自person中的方法。 但是目前有一个很大的弊端:某些属性其实是保存在p对象上的;
第一,我们通过直接打印对象是看不到这个属性的;
第二,这个属性会被多个对象共享,如果这个对象是一个引用类型,那么就会造成问题;
第三,不能给Person传递参数,因为这个对象是一次性创建的(没办法定制化);
借用构造函数继承为了解决原型链继承中存在的问题,开发人员提供了一种新的技术: constructor stealing(有很多名称: 借用构造函数或者称之为经典继承或者称之为伪造对象);steal是偷窃、剽窃的意思,但是这里可以翻译成借用; 借用继承的做法非常简单:在子类型构造函数的内部调用父类型构造函数. 因为函数可以在任意的时刻被调用; 因此通过apply()和call()方法也可以在新创建的对象上执行构造函数;
// 父类: 公共属性和方法
function Person(name, age, friends) {
// this = stu
this.name = name
this.age = age
this.friends = friends
}
Person.prototype.eating = function() {
console.log(this.name + " eating~")
}
// 子类: 特有属性和方法
function Student(name, age, friends, sno) {
Person.call(this, name, age, friends)
// this.name = name
// this.age = age
// this.friends = friends
this.sno = 111
}
var p = new Person()
Student.prototype = p
Student.prototype.studying = function() {
console.log(this.name + " studying~")
}七,原型继承关系
