2013-06-17 5 views
0

Как работает прототип?Вопросы относительно прототипа функции

var object, u1, u2; 
object = function(o) { 
    var F = function() {}; 
    F.prototype = o; 
    return new F(); 
}; 
u1 = {'name': 'adam'}; 
u2 = object(u1); 
u2.name = 'roman'; 
delete u1.name; 
console.log(u2.name); 
delete u2.name; 
console.log(u2.name); 
u1.name = 'tytus'; 
console.log(u2.name); 

roman 
undefined 
titus 

Почему производится вывод третьего console.log? Не могли бы вы объяснить это поведение?

ответ

4

Это связано с тем, как JS разрешает ссылки или оценивает выражения. То, что происходит примерно так:

u2[name] ===> JS checks instance for property name<------------|| 
    || --> property not found @instance, check prototype (u1) || 
    ===========> u1.name was found, return its value ----------|| 
     || 
     || if it weren\'t found here: 
     ==========> Object.prototype.name: not found check prototype? 
      || 
      =======>prototype of object.prototype is null, return undefined 

При входе u2.name круглый первый раз, name было свойство экземпляра, поэтому JS разрешило выражение на эту строку постоянной (roman). Затем вы удалили его, поэтому, пытаясь снова получить к нему доступ, JS решила создать прототип u1.name (u2), который также не имел свойства name и не нашел Object.prototype.name, поэтому выражение было разрешено на undefined.

В третьем случае, повторяли одни и те же шаги, только на этот раз прототип u2сделал имеет name свойство, так это то, что выражение было решено. Это так просто, действительно.
Хорошо, мне потребовалось некоторое время, чтобы привыкнуть к этому, но как только вы его получите, это чистая логика.

BTW, диаграмма, которую я сделал здесь, представляет собой визуальное представление того, что называется «прототип-цепочка». С точки зрения u2, цепь u1 -> Object, которая не является очень большой цепи на всех (в случае, если вы были обеспокоены): это очень похоже на цепь с Array например:

console.log(Object.getPrototypeOf(Array.prototype));//Logs Object 

Следовательно, цепочка прототипов экземпляра массива, скажем var foo = [];) выглядит следующим образом:

Array.prototype => Object.prototype 

это означает, что:

foo = [] 
foo[123];//does not exists 

Триггеры как imilar поиск через цепочку прототипов для того, чтобы выражение для решения в undefined:

foo[123] ===> 123 is coerced to string first BTW <------------|| 
    || --> property not found @instance, check Array.prototype || 
    ===========> Array.prototype[123] not found    || 
     ||             || 
     ==========> no Object.prototype[123]: check prototype || 
      ||            || 
      =======>prototype of object.prototype is null, return undefined 
+1

хорошее описание +1 – Vinay

1

При попытке доступа к свойству объекта, JavaScript сначала проверяет, если реальный объект имеет то свойство, а если нет, то он проверяет если прототип его функции-конструктора обладает этим свойством:

var object, u1, u2; 
object = function(o) { 
    var F = function() {}; 
    F.prototype = o; 
    return new F(); 
}; 

//u1 is just a "normal" JavaScript Object, created by the Object constructor function 
u1 = {'name': 'adam'}; 
//u2 is an object, that was created by a custom constructor function (F) with the prototype u1 
u2 = object(u1); 

//add a local property "name" to u2 
u2.name = 'roman'; 

//delete the property "name" from u1 (and thereby from the prototype of u2) 
delete u1.name; 

//prints "roman" because u2 has a local property "name" 
console.log(u2.name); 

//delete the local property "name" from u2 
delete u2.name; 

//neither u2 nor the prototype of u2 have a property name -> undefined 
console.log(u2.name); 

//add a property "name" to u1 (and thereby to the prototype of u2) 
u1.name = 'tytus'; 

//prints "tytus" because u2 doesn't have a local property and therefor the property "name" of its prototype is used. 
console.log(u2.name); 
2

JavaScript - это объектно-ориентированный язык программирования. Однако, в отличие от других объектно-ориентированных языков, таких как C++ и Python, у него нет классов. Вместо этого у JavaScript есть прототипное наследование.

В прототипальных объектно-ориентированных языках программирования у вас есть только объекты.Наследование осуществляется с помощью одного из двух способов:

  1. Delegation
  2. Concatenation

Предположим, что у меня есть объект, называемый rectangle следующим образом:

var rectangle = { 
    height: 5, 
    width: 10, 
    area: function() { 
     return this.width * this.height; 
    } 
}; 

Теперь я могу вычислить площадь этот прямоугольник, вызвав rectangle.area. Однако что, если бы я хотел создать еще один прямоугольник с разными width и height? Здесь используется ваша object функции (эта функция доступна изначально в большинстве интерпретаторов JavaScript в Object.create):

var rectangle2 = Object.create(rectangle); 

rectangle2.height = 10; 
rectangle2.width = 20; 

alert(rectangle2.area()); // 200 

То, что здесь происходит, что объект rectangle2 наследует от объекта rectangle посредством делегирования. Для того, чтобы помочь вам визуализировать то, что происходит посмотреть на следующей диаграмме:

   null 
       ^
       | [[Proto]] 
       | 
+-------------------------------+ 
|  Object.prototype  | 
+-------------------------------+ 
|    ...    | 
+-------------------------------+ 
       ^
       | [[Proto]] 
       | 
+-------------------------------+ 
|   rectangle   | 
+-------------------------------+ 
|   height: 5   | 
+-------------------------------+ 
|   width: 10   | 
+-------------------------------+ 
|   area: ...   | 
+-------------------------------+ 
       ^
       | [[Proto]] 
       | 
+------------------------------+ 
|   rectangle2   | 
+------------------------------+ 
|   height: 10   | 
+------------------------------+ 
|   width: 20   | 
+------------------------------+ 

Object.create функция создает объект (rectangle2), внутренняя [[Proto]] свойство указывает на объект rectangle (прототип rectangle2).

При попытке получить доступ к объекту недвижимости на rectangle2 JavaScript сначала пытается найти недвижимость на rectangle2. Если он не может найти недвижимость на rectangle2 тогда он пытается найти его на прототипе rectangle2 (т.е. rectangle) и так далее до тех пор:

  1. Он не найти недвижимость, в этом случае она возвращает значение, связанное с это свойство.
  2. Он исчерпывает цепь прототипа (то есть достигает null), и в этом случае он возвращает undefined.

Следовательно, если я попытаюсь получить доступ rectangle2.width, он вернется 20. Однако, если я попытаюсь получить доступ к rectangle2.area, он вернет функцию rectangle.area вместо undefined. Это прототипное наследование для вас в двух словах.


Теперь у вас код сначала создать объект u1:

var u1 = { 
    name: "adam" 
}; 

Затем вы создаете объект u2, который наследуется от u1:

var u2 = Object.create(u1); 

После этого вы установили новый name имущество на u2 (это не перезаписывает name недвижимость u1, это просто тени его):

u2.name = "roman"; 

Затем удалить name свойство u1 (это не влияет на свойство имени u2):

delete u1.name; 

Так при входе u2.name он отображает roman:

console.log(u2.name); // roman 

Затем удалить name свойство из u2, а также:

delete u2.name; 

Поэтому при входе u2.name снова отображается undefined:

console.log(u2.name); // undefined 

Наконец вы установили u1.name в tytus:

u1.name = "tytus"; 

Поэтому при входе u2.name снова его отображает tytus, потому что u2 делегирует u1:

console.log(u2.name); // tytus 

Я надеюсь, что это помогло вам понять прототипичное наследство. Если вы хотите узнать больше, прочитайте мой пост в блоге Why Prototypal Inheritance Matters.