Как правило, объекты создаются непосредственно в форме литералов, но этот метод создания будет генерировать много повторяющегося кода при создании большого количества похожих объектов. Но js отличается от обычных объектно-ориентированных языков тем, что до ES6 в нем не было понятия класса. Тем не менее, функции могут использоваться для моделирования для создания повторно используемых методов создания объектов.
1. Заводской режим
//搞一个工厂函数反复调用
function createPerson(name, job) {
var o = new Object();
o.name = name;
o.job = job;
o.sayName = function() {
console.log(this.name);
}
return o;
}
var person1 = createPerson('Mike', 'student');
var person2 = createPerson('X', 'engineer');
//总结:工厂模式虽然解决了创建多个相似对象的问题,但是没有解决对象识别问题,即不能知道一个对象的类型
2. Режим конструктора
//没有显示的创建对象,使用new来调用这个构造函数,使用new后会自动执行如下操作:
/*
①创建一个新对象;
②将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
③执行构造函数中的代码(为这个新对象添加属性);
④返回新对象。
*/
function Person(name, job) {
this.name = name;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
}
var person1 = new Person('Mike', 'student');
var person2 = new Person('X', 'engineer');
/*************************************************************************************/
//缺点:每个方法都要在每个实例上重新创建一遍。解决方法如下:
//创建两个完成同样任务的的 Function 实例的确没有必要。况且有 this 对象在,根本不用在执行代码前就把函数绑定到特定的对象上
function Person( name, age, job ){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;//函数指针,这样实例就会共享一个函数地址
}
function sayName(){
alert( this.name );
}
//这样也有问题
//1.全局作用域中定义的函数(sayName)实际上只能被某个对象调用,这让全局作用域有点名不副实。
//2.如果对象需要定义很多方法,那么就需要定义很多个全局函数,这样一来,我们自定义的这个引用类型就毫无封装性可言了。
//这些问题可以通过使用原型模式来解决。⬇
3. Образец прототипа
//将信息直接添加到原型对象上。
//使用原型的好处是可以让所有的实例对象共享它所包含的属性和方法,不必在构造函数中定义对象实例信息,而是可以将这些信息直接添加到原型对象中。
function Person() { }
Person.prototype.name = 'Mike';//这样可就每个人都叫MIKE了哈
Person.prototype.job = 'student';
Person.prototype.sayName = function() {
console.log(this.name)
}
var person1 = new Person();
//还有更简单的写法
function Person(){ }
Person.prototype = {
constructor = Person()//设置新原型,再将constructor指回构造函数
name : "Mike",
age : 29,
job : "engineer",
syaName : function(){
alert( this.name );
}
};
Ошибочность прототипа
Всякий раз, когда создается новая функция, для этой функции создается свойство-прототип в соответствии с определенным набором правил. По умолчанию все свойства прототипа автоматически получают свойство конструктора (функции конструктора), которое содержит указатель на функцию, в которой находится свойство прототипа.
Всякий раз, когда код считывает свойство объекта, выполняется поиск свойства с заданным именем. Поиск начинается с самого экземпляра объекта. Если атрибут с заданным именем найден в экземпляре, вернуть значение атрибута, если не найден, продолжить поиск объекта-прототипа, на который указывает указатель, и найти атрибут с заданным именем в объекте-прототипе. Если свойство найдено в объекте-прототипе, возвращается значение свойства.
Хотя к значениям, хранящимся в прототипе, можно получить доступ через экземпляр объекта, значения в прототипе нельзя перезаписать через экземпляр объекта. Если мы добавим в экземпляр свойство с тем же именем, что и свойство в экземпляре, это свойство будет создано в экземпляре, и это свойство будет затенять свойство в прототипе.
Даже установка свойства на null приведет к тому, что значение свойства будет нулевым только в экземпляре. Однако свойства экземпляра можно полностью удалить с помощью оператора удаления, что позволит повторно получить доступ к свойствам в прототипе. Используйте метод hasOwnProperty(), чтобы определить, существует ли свойство в экземпляре или в прототипе. Этот метод вернет true только в том случае, если данное свойство существует в экземпляре объекта.
4. Сочетание режима конструктора и режима прототипа
//组合使用构造函数模式和原型模式是使用最为广泛、认同度最高的一种创建自定义类型的方法。
function Person(name) {
this.name = name;
this.friends = ['Jack', 'Merry'];
}
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push('Van');
console.log(person1.friends); //["Jack", "Merry", "Van"]
console.log(person2.friends); // ["Jack", "Merry"]
console.log(person1.friends === person2.friends); //false
//值得注意,sayName中的this指向调用它的对象,可不是指向定义它的作用域,箭头函数的this指向定义它的作用域。
5. Режим динамического прототипа
//动态原型模式将所有信息都封装在了构造函数中,初始化的时候,可以通过检测某个应该存在的方法是否有效,来决定是否需要初始化原型。
//特点:节约第二次及以后调用的时间
function Person(name, job) {
// 属性
this.name = name;
this.job = job;
// 方法
if(typeof this.sayName !== 'function') {
Person.prototype.sayName = function() {
console.log(this.name)
}
}
}
var person1 = new Person('Mike', 'Student');
person1.sayName();
//只有在 sayName() 方法不存在的时候,才会将它添加到原型中。这段代码只会初次调用构造函数的时候才会执行。此后原型已经完成初始化,不需要在做什么修改了,这里对原型所做的修改,能够立即在所有实例中得到反映。
//意思就是只要有一个if在这了,只有第一次调用Person时候才给原型绑定,避免重复操作。
6. Режим паразитного конструктора
function Person(name, job) {
var o = new Object();
o.name = name;
o.job = job;
o.sayName = function() {
console.log(this.name)
}
return o;
}
var person1 = new Person('Mike', 'student');
person1.sayName();
//这个模式,除了使用 new 操作符并把使用的包装函数叫做构造函数之外,和工厂模式几乎一样。
//原理:
//构造函数如果不返回对象,默认也会返回一个新的对象,通过在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值。原本构造函数返回的是this
//注意:
//这样创建出来的对象与构造函数之间没有什么关系, instanceof 操作符对他们没有意义。