一,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方法获取属性描述符。
  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false;

[[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性;

  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false;

[[Writable]]:表示是否可以修改属性的值;

  • 当我们直接在一个对象上定义某个属性时,这个属性的[[Writable]]为true;
  • 当我们通过属性描述符定义一个属性时,这个属性的[[Writable]]默认为false;

[[value]]:属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改;

  • 默认情况下这个值是undefined;

此外,我们还可以通过Object.defineProperties() 方法直接在一个对象上定义 多个 新的属性或修改现有属性,并且返回该对象

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,构造函数模式

  • 在内存中创建一个新的对象(空对象)
  • 这个对象的内部的proto属性会被赋值为这个构造函数的prototype属性
  • 构造函数内部的this会被指向创建出来的新对象
  • 执行构造函数内部的方法体
  • 返回创建出来的新对象

通过构造函数创建多个对象就没有缺点了么?有,它在于我们需要为每个对象的函数去创建一个函数对象实例,比如说小明有eating方法而小红有running方法但是这样创建的话会使小明不仅有eating还有running。并且小明的是小明的,小红的是小红的,没有实现共用。

3,构造函数和原型相组合方式:将多个对象中相同的方法放到构造函数的原型上去。

  • 当我们通过引用对象的属性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中可以通过原型链来实现继承

  • 第一,我们通过直接打印对象是看不到这个属性的;
  • 第二,这个属性会被多个对象共享,如果这个对象是一个引用类型,那么就会造成问题;
  • 第三,不能给Person传递参数,因为这个对象是一次性创建的(没办法定制化);

借用构造函数继承为了解决原型链继承中存在的问题,开发人员提供了一种新的技术: constructor stealing(有很多名称: 借用构造函数或者称之为经典继承或者称之为伪造对象);steal是偷窃、剽窃的意思,但是这里可以翻译成借用; 借用继承的做法非常简单:在子类型构造函数的内部调用父类型构造函数. 因为函数可以在任意的时刻被调用; 因此通过apply()和call()方法也可以在新创建的对象上执行构造函数;