Я помню, как раньше видел вопрос о Zhihu:Интервью с 5-летним фронтендомСеть прототиповЯ не могу понять, там полно Vue, React и других реализаций, стоит ли таким людям его использовать?. Когда я писал статью, я вернулся и прочитал этот вопрос, было более 300 ответов, и многие большие ребята ответили на этот вопрос, что показывает, что этот вопрос все еще привлекает много внимания. В последние годы, с ростом популярности ES6, TypeScript и подобных промежуточных языков, у нас редко есть доступ к прототипам в бизнес-разработке.В основном мы используем классы ES6, которые приходят и уходят более простым и интуитивно понятным способом.Прототипы делают.
На самом деле, на мой взгляд, я считаю цепочку прототипов очень важным базовым знанием. Если человек говорит, что владеет языком C, но не знаком с ассемблером, вы ему поверите? Я думаю, что Зима сказала довольно кратко:
Это также включает в себя навыки интервьюера, о которых я говорил ранее.Интервью представляет собой систематическую оценку способности человека выяснить, в чем он хорош, систематически спрашивать.
Прототипы могут очень хорошо объяснить проблему, как минимум у него будет большой минус в дизайне библиотек, и могут быть проблемы с обучаемостью, или он может вообще не знать язык JS, но это не значит, что полагаясь на проблему Можно судить, что этого человека нельзя использовать.
Эта статья включает в себя следующее:
- JavaScript-прототип
- конструктор и
prototype
- Сеть прототипов
- Использование прототипа
- Связь между классами ES6 и конструкторами
- прототипное наследование
- Особенности синтаксиса JavaScript и прототипов
- Загрязнение прототипа
- Добавьте вопрос интервью, связанный с прототипом, с которым вы столкнулись на недавнем собеседовании при приеме на работу в школу.
JavaScript-прототип
Прототип должен называться по-английскиprototype
, у любого объекта есть прототип, мы можем передавать нестандартные свойства__proto__
Чтобы получить доступ к прототипу объекта:
// 纯对象的原型默认是个空对象
console.log({}.__proto__); // => {}
function Student(name, grade) {
this.name = name;
this.grade = grade;
}
const stu = new Student('xiaoMing', 6);
// Student 类型实例的原型,默认也是一个空对象
console.log(stu.__proto__); // => Student {}
__proto__
Это нестандартное свойство, если вы хотите получить доступ к прототипу объекта, рекомендуется использовать новый ES6Reflect.getPrototypeOf
илиObject.getPrototypeOf()
метод. Нестандартный атрибут означает, что атрибут может быть напрямую изменен или удален в будущем, возможно, в будущем будет использоваться новый стандарт.Symbol.proto
В качестве ключа для доступа к прототипу объекта это нестандартное свойство может быть удалено.
console.log({}.__proto__ === Object.getPrototypeOf({})); // => true
мы можем пройти__proto__
Измените прототип объекта, назначив свойства напрямую. Более рекомендуемый подход — использовать ES6.Reflect.setPrototypeOf
илиObject.setPrototypeOf
. В любом случае тип устанавливаемого значения может быть только объектом или нулевым, другие типы не работают:
const obj = { name: 'xiaoMing' };
// 原型为空对象
console.log(obj.__proto__); // => {}
obj.__proto__ = 666;
// 非对象和 null 不生效
console.log(obj.__proto__); // => {}
// 设置原型为对象
obj.__proto__ = { a: 1 };
console.log(obj.__proto__); // => { a: 1 }
console.log(Reflect.getPrototypeOf(obj)); // => { a: 1 }
Выдает, если устанавливаемое значение не является расширяемымTypeError
:
const frozenObj = Object.freeze({});
// Object.isExtensible(obj) 可以判断 obj 是不是可扩展的
console.log(Object.isExtensible(frozenObj)); // => false
frozenObj.__proto__ = null; // => TypeError: #<Object> is not extensible
если объект__proto__
При присвоении значения null ситуация усложняется, посмотрите на следующий тест, он может показаться вам странным:
const obj = { name: 'xiaoming' };
obj.__proto__ = null;
// !: 为什么不是 null, 就好像 __proto__ 被 delete 了
console.log(obj.__proto__); // => undefined
// 说明确实将原型设置为 null 了
console.log(Reflect.getPrototypeOf(obj)); // => null
// 再次赋值为 null
obj.__proto__ = null;
// 黑人问号???咋不是之前的 undefined 呢?
console.log(obj.__proto__); // => null
obj.__proto__ = { a: 1 };
console.log(obj.__proto__); // => { a: 1 }
// __proto__ 就像一个普通属性一样 obj.xxx = { a: 1 }
// 并没有将原型设置成功
console.log(Reflect.getPrototypeOf(obj)); // => null
Reflect.setPrototypeOf(obj, { b: 2 });
// __proto__ 被设置为 null 后,obj 的 __proto__ 属性和一个普通的属性没有区别
console.log(obj.__proto__); // => { a: 1 }
// 使用 Reflect.setPrototypeOf 是可以设置原型的
console.log(Reflect.getPrototypeOf(obj)); // => { b: 2 }
фактически__proto__
определяется вObject.prototype
свойства средства доступа на , то есть с помощьюgetter
а такжеsetter
свойства, определяемые__proto__
изgetter
мы можем получить объект[[Prototype]]
, который является прототипом. Ниже моя симуляция__proto__
Код поведения, обратите внимание на случай, когда в следующем коде установлено значение null:
const weakMap = new WeakMap();
Object.prototype = {
get __proto__() {
return this['[[prototype]]'] === null ? weakMap.get(this) : this['[[prototype]]'];
},
set __proto__(newPrototype) {
if (!Object.isExtensible(newPrototype)) throw new TypeError(`${newPrototype} is not extensible`);
const isObject = typeof newPrototype === 'object' || typeof newPrototype === 'function';
if (newPrototype === null || isObject) {
// 如果之前通过 __proto__ 设置成 null
// 此时再通过给 __proto__ 赋值的方式修改原型都是徒劳
/// 表现就是 obj.__proto__ = { a: 1 } 就像一个普通属性 obj.xxx = { a: 1 }
if (this['[[prototype]]'] === null) {
weakMap.set(this, newPrototype);
} else {
this['[[prototype]]'] = newPrototype;
}
}
},
// ... 其它属性如 toString,hasOwnProperty 等
};
В общем: если объект__proto__
имущество назначается какnull
, его прототип в настоящее время действительно изменен на null, но вы хотите передать__proto__
Недопустимый метод присваивания при установке прототипа.В настоящее время__proto__
ничем не отличается от обычного свойства, только черезReflect.setPrototypeOf
илиObject.setPrototypeOf
модифицировать прототип. Прототип — это свойство внутри объекта[[prototype]]
,а такжеReflect.setPrototypeOf
Причина, по которой прототип может быть изменен, заключается в том, что он напрямую изменяет свойство прототипа объекта, то есть внутреннее свойство объекта.[[prototype]]
передача имущества без прохождения__proto__
изgetter
.
Конструкторы и прототипы
Конструктор на английском естьconstructor
, в JavaScript,Функции могут использоваться как конструкторы. Конструктор также можно назвать классом, а конструктор Student можно назвать классом Student. Мы можем создать экземпляр с помощью нового конструктора. По соглашению мы используем большой верблюжий кейс для функций, используемых в качестве конструкторов:
function Apple() {}
const apple = new Apple();
console.log(apple instanceof Apple); // => true
У любого конструктора есть свойство прототипа, которое по умолчанию является пустым чистым объектом, и прототип всех экземпляров, созданных конструктором, указывает на него.
// 实例的原型即 apple1.__proto__
console.log(apple1.__proto__ === Apple.prototype); // => true
console.log(apple2.__proto__ === Apple.prototype); // => true
Следующие результаты тестов могут доказать, что свойство прототипа конструктора по умолчанию является пустым объектом.Обратите внимание, что пустой объект здесь означает, что объект не имеет свойств, которые можно пройти:
console.log(Apple.prototype); // => Apple {}
console.log(Object.keys(Apple.prototype)); // => []
console.log(Apple.prototype.__proto__ === {}.__proto__); // true
Конструкторprototype
есть одинconstructor
свойство, указывающее на сам конструктор:
console.log(Apple.prototype.constructor === Apple); // => true
этоconstructor
Свойство не проходное, можно понять, что свойство определяется следующим образом:
Object.defineProperty(Apple.prototype, 'constructor', {
value: Student,
writable: true,
// 不可枚举,无法通过 Object.keys() 获取到
enumerable: fasle,
});
__proto__
,prototype
,constructor
,Apple
функция, примерapple
и прототип объекта[[prototype]]
Отношение между:
Некоторые люди могут поставить__proto__
а такжеprototype
Запутать. С точки зрения перевода их обоих можно назвать прототипами, но на самом деле это две совершенно разные вещи.
__proto__
существует на всех объектах,prototype
Существует для всех функций, и их отношение таково: функцияprototype
все экземпляры созданы с использованием новой функции__proto__
. Функции также являются объектами, поэтому функции также имеют__proto__
а такжеprototype
.
Уведомление: Если я упоминаю прототип конструктора в своей статье, это относится к прототипу конструктора__proto__
, а не свойство прототипа конструктора.
Сеть прототипов
Такпрототип объектаКаковы характеристики?
При доступе к свойству объекта obj, если оно не существует в obj, оно перейдет к прототипу объекта, который
obj.__proto__
Найдите это свойство. Если есть, вернуть это свойство, если нет, перейти к прототипу прототипа объекта obj, которыйobj.__proto__.__proto__
Найдите его и повторите описанные выше шаги. до визитачистый объектПрототипObject.prototype
, если нет, продолжайте поискObject.prototype.__proto__
, который на самом деле равен null, напрямую возвращает значение undefined.
Например:
function Student(name, grade) {
this.name = name;
this.grade = grade;
}
const stu = new Student();
console.log(stu.notExists); // => undefined
доступstu.notExists
Весь процесс таков:
- Первый взгляд
stu
Существует ли наnotExists
, не существует, поэтому см.stu.__proto__
-
stu.__proto__
не существуетnotExists
свойства см.stu.__proto__.__proto__
, по фактучистый объектПрототип:Object.prototype
-
чистый объектне существует в прототипе
notExists
свойства, дальше вверх, кstu.__proto__.__proto__.__proto__
Поднимитесь, чтобы найти его, на самом деле он нулевой - ноль не существует
notExists
свойство, возвращает неопределенное
У некоторых читателей после прочтения вышеизложенного могут возникнуть сомнения, а прототип объекта будет найден в концечистый объектПрототип? Просто проверьте это:
console.log(stu.__proto__.__proto__ === {}.__proto__); // => true
чистый объектПрототип прототипа равен нулю:
console.log(new Object().__proto__.__proto__); // => null
Цепочка, образованная между каждым прототипом, называется цепочкой прототипов.
Подумайте об этом, функцияStudent
Как должна выглядеть цепочка прототипов?
Использование прототипа
При использовании конструктора для определения типа мы обычно определяем метод класса в прототипе, что идеально подходит для функции указания this.
function Engineer(workingYears) {
this.workingYears = workingYears;
}
// 不能使用箭头函数,箭头函数的 this 在声明的时候就根据上下文确定了
Engineer.prototype.built = function () {
// this 这里就是执行函数调用者
console.log(`我已经工作 ${this.workingYears} 年了, 我的工作是拧螺丝...`);
};
const engineer = new Engineer(5);
// this 会正确指向实例,所以 this.workingYears 是 5
engineer.built(); // => 我已经工作 5 年了, 我的工作是拧螺丝...
console.log(Object.keys(engineer)); // => [ 'workingYears' ]
Таким образом, все экземпляры могут получить доступ к этому методу, и этот метод должен только занимать кусок памяти, экономя память, и указатель this также может правильно указывать на экземпляр класса.
Однако к методу, определенному таким образом, нельзя получить доступ через Object.keys(), в конце концов, это не его собственное свойство:
const obj = {
func() {},
};
console.log(Object.keys(obj)); // => [ 'func' ]
function Func() {}
Func.prototype.func = function () {};
console.log(Object.keys(new Func())); // => []
Если вы просто хотите определить свойства экземпляра, вы можете передать толькоthis.xxx = xxx
способ определения методов экземпляра:
function Engineer(workingYears) {
this.workingYears = workingYears;
this.built = function () {
console.log(`我已经工作 ${this.workingYears} 年了, 我的工作是拧螺丝...`);
};
}
const engineer = new Engineer(5);
console.log(Object.keys(engineer)); // => [ 'workingYears', 'built' ]
На самом деле многие методы в JavaScript определены в прототипе конструктора, например, Array.prototype.slice, Object.prototype.toString и т. д.
Связь между классами ES6 и конструкторами
Многие языки имеют парадигмы объектно-ориентированного программирования, такие как java, c#, python и т. д. Классы ES6 упрощают объектно-ориентированное программирование для разработчиков, переходящих от них к JavaScript.
ES6 class
фактически,Класс ES6 — это синтаксический сахар для конструкторов.. Давайте посмотрим, во что Babel компилирует классы ES6:
Оригинальный код:
class Circle {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
draw() {
console.log(`画个坐标为 (${this.x}, ${this.y}),半径为 ${this.r} 的圆`);
}
}
babel + babel-preset-es2015-loose
Скомпилированный результат:
'use strict';
// Circle class 可以理解为就是一个构造器函数
var Circle = (function () {
function Circle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
var _proto = Circle.prototype;
// class 方法定义在 prototype 上
_proto.draw = function draw() {
console.log(
'\u753B\u4E2A\u5750\u6807\u4E3A (' +
this.x +
', ' +
this.y +
')\uFF0C\u534A\u5F84\u4E3A ' +
this.r +
' \u7684\u5706'
);
};
return Circle;
})();
С первого взгляда становится ясно, что класс ES6 является конструктором, а методы класса определены в прототипе конструктора.
расширяет наследование
Давайте посмотрим на использованиеextends
Как преобразовать при наследовании.
Оригинальный код:
class Shape {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Circle extends Shape {
constructor(x, y, r) {
super(x, y);
this.r = r;
}
draw() {
console.log(`画个坐标为 (${this.x}, ${this.y}),半径为 ${this.r} 的圆`);
}
}
babel + babel-preset-es2015-loose
Скомпилированный результат:
'use strict';
// 原型继承
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
// 让子类可以访问父类上的静态属性,其实就是定义在构造器自身上的属性
// 例如父类有 Person.say 属性,子类 Student 通过可以通过 Student.say 访问
subClass.__proto__ = superClass;
}
var Shape = function Shape(x, y) {
this.x = x;
this.y = y;
};
var Circle = (function (_Shape) {
_inheritsLoose(Circle, _Shape);
function Circle(x, y, r) {
var _this;
// 组合继承
_this = _Shape.call(this, x, y) || this;
_this.r = r;
return _this;
}
var _proto = Circle.prototype;
_proto.draw = function draw() {
console.log(
'\u753B\u4E2A\u5750\u6807\u4E3A (' +
this.x +
', ' +
this.y +
')\uFF0C\u534A\u5F84\u4E3A ' +
this.r +
' \u7684\u5706'
);
};
return Circle;
})(Shape);
Все расширения ES6 реализуют прототипное наследование + наследование композиции.
Конструктор подкласса вызывает конструктор родительского класса и указывает его экземпляру подкласса для достиженияОбъединить свойства экземпляра родительского класса с экземпляром дочернего класса:
// 组合继承
_this = _Shape.call(this, x, y) || this;
_inheritsLoose
Эта функция реализует прототипное наследование, описанное в следующем разделе.
прототипное наследование
говорящий原型继承
Прежде чем мы поговорим继承
эта фраза. Я думаю, в общем смысле继承
говорит:Если класс A наследуется от класса B, то экземпляры A наследуют свойства экземпляра B..
原型继承
это继承
С точки зрения простого继承
Не совсем то же самое, это:Экземпляры A могут наследовать свойства прототипа B.
Давайте определим прототипное наследование:
对于类 A 和类 B,如果满足 A.prototype.__proto__ === B.prototype,那么 A 原型继承 B
На самом деле приведенное выше определение является слишком строгим, я думаю, пока прототип B находится в цепочке прототипов A, этого достаточно, чтобы свойства прототипа B уже были доступны для экземпляра A. Можно сказать, что приведенное выше определение наследуется напрямую, но может быть два или более уровня наследования.
Как реализовать прототипное наследование? Самый простой способ - установить его напрямуюA.prototype === new B()
, пусть прототип A будет экземпляром B:
function A() {}
function B() {
this.xxx = '污染 A 的原型';
}
A.prototype = new B();
console.log(A.prototype.__proto__ === B.prototype); // => true
Но этот способ приведет к тому, что свойства экземпляра B загрязнят прототип A. Решение состоит в том, чтобы связать его с пустой функцией.Пустая функция никогда не загрязнит цепочку прототипов атрибутами экземпляра:
function A(p) {
this.p = p;
}
function B() {
this.xxx = '污染原型';
}
// 空函数
function Empty() {}
Empty.prototype = B.prototype;
A.prototype = new Empty();
// 修正 constructor 指向
A.prototype.constructor = A;
// 满足原型继承的定义
console.log(A.prototype.__proto__ === B.prototype); // => true
const a = new A('p');
console.log(a instanceof A); // => true
const b = new B();
console.log(b instanceof B); // => true
// a 也是 B 的实例
console.log(a instanceof B); // => true
console.log(a.__proto__.__proto__ === B.prototype); // => true
Цепочка прототипов, нарисованная с помощью программного обеспечения для рисования, которое поставляется с Windows_〆(´Д ` ):
использоватьObject.create
, мы можем реализовать прототипное наследование более просто, что является функцией инструмента, используемой Babel выше._inheritsLoose
:
function _inheritsLoose(subClass, superClass) {
// Object.create(prototype) 返回一个以 prototype 为原型的对象
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
// 我们上面实现的原型继承没有设置这个,但是 class 的继承会设置子类的原型为父类
subClass.__proto__ = superClass;
}
Особенности синтаксиса JavaScript и прототипов
На самом деле, многие грамматические особенности связаны с прототипами.Что касается прототипов, мы продолжим говорить о некоторых знаниях, связанных с прототипами в грамматических функциях JavaScript.
новый операторный принцип
Что происходит, когда мы используем new в функции.
Использование кода для его описания:
function isObject(value) {
const type = typeof value;
return value !== null && (type === 'object' || type === 'function');
}
/**
* constructor 表示 new 的构造器
* args 表示传给构造器的参数
*/
function New(constructor, ...args) {
// new 的对象不是函数就抛 TypeError
if (typeof constructor !== 'function') throw new TypeError(`${constructor} is not a constructor`);
// 创建一个原型为构造器的 prototype 的空对象 target
const target = Object.create(constructor.prototype);
// 将构造器的 this 指向上一步创建的空对象,并执行,为了给 this 添加实例属性
const result = constructor.apply(target, args);
// 上一步的返回如果是对象就直接返回,否则返回 target
return isObject(result) ? result : target;
}
Просто проверьте это:
function Computer(brand) {
this.brand = brand;
}
const c = New(Computer, 'Apple');
console.log(c); // => Computer { brand: 'Apple' }
Принцип оператора instanceof
instanceof используется для определения того, является ли объект экземпляром класса, если obj instance A, мы говорим, что obj является экземпляром A.
Его принцип очень прост, в одном предложении:obj instanceof конструктора A, что эквивалентно оценке того, является ли прототип A прототипом obj (он также может быть вторичным прототипом).
Код:
function instanceOf(obj, constructor) {
if (!isObject(constructor)) {
throw new TypeError(`Right-hand side of 'instanceof' is not an object`);
} else if (typeof constructor !== 'function') {
throw new TypeError(`Right-hand side of 'instanceof' is not callable`);
}
// 主要就这一句
return constructor.prototype.isPrototypeOf(obj);
}
Просто проверьте это:
function A() {}
const a = new A();
console.log(a instanceof A); // => true
console.log(instanceOf(a, A)); // => true
Загрязнение прототипа
Осенью 2019 года прошлого года, когда я еще был стажером на крупном отечественном заводе, lodash взломал серьезную уязвимость в системе безопасности:Серьезная уязвимость в библиотеке Lodash, затрагивающая более 4 миллионов проектов.. Это нарушение безопасности связано сЗагрязнение прототипавызванный.
Загрязнение прототипа относится к:
Злоумышленник каким-то образом модифицирует прототип объекта JavaScript.
Хотя можно сказать, что любой прототип загрязнен, это может вызвать проблемы, но обычно мы называем загрязнение прототипаObject.prototype
загрязненный.
Опасности загрязнения прототипа
проблемы с производительностью
Возьмем самый простой пример:
Object.prototype.hack = '污染原型的属性';
const obj = { name: 'xiaoHong', age: 18 };
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(obj[key]);
}
}
/* =>
xiaoHong
18
*/
Если прототип загрязнен, это увеличит количество обходов.Каждый раз, когда вы обращаетесь к свойству, которого нет в самом объекте, вы также должны обращаться к загрязненному свойству на следующем прототипе.
вызывать неожиданные логические ошибки
Посмотрите на конкретный случай уязвимости безопасности узла:
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const path = require('path');
const isObject = (obj) => obj && obj.constructor && obj.constructor === Object;
function merge(a, b) {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a;
}
function clone(a) {
return merge({}, a);
}
// Constants
const PORT = 8080;
const HOST = '127.0.0.1';
const admin = {};
// App
const app = express();
app.use(bodyParser.json());
app.use(cookieParser());
app.use('/', express.static(path.join(__dirname, 'views')));
app.post('/signup', (req, res) => {
var body = JSON.parse(JSON.stringify(req.body));
var copybody = clone(body);
if (copybody.name) {
res.cookie('name', copybody.name).json({
done: 'cookie set',
});
} else {
res.json({
error: 'cookie not set',
});
}
});
app.get('/getFlag', (req, res) => {
var аdmin = JSON.parse(JSON.stringify(req.cookies));
if (admin.аdmin == 1) {
res.send('hackim19{}');
} else {
res.send('You are not authorized');
}
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
Уязвимость этого кода заключается в функции слияния, мы можем атаковать ее так:
curl -vv --header 'Content-type: application/json' -d '{"__proto__": {"admin": 1}}' 'http://127.0.0.1:4000/signup';
curl -vv 'http://127.0.0.1/getFlag'
сначала запросить/signup
интерфейс, в сервисе NodeJS мы называем уязвимыйmerge
метод и через__proto__
дляObject.prototype
(потому что{}.__proto__ === Object.prototype
), чтобы добавить предыдущий новый атрибутadmin
, а значение равно 1.
запросить сноваgetFlag
интерфейс, который обращается к прототипу объекта наadmin
,Условные операторыadmin.аdmin == 1
дляtrue
, служба атакована.
Предотвращение загрязнения прототипа
Фактически, загрязнение прототипа в основном происходит при вызове функций, которые изменяют или расширяют свойства объекта, такие как значения по умолчанию в lodash и расширение в jquery. Самое главное для предотвращения загрязнения прототипа — иметь чувство предотвращения и выработать хорошие привычки кодирования.
Object.create(null)
Когда я смотрю на исходный код некоторых библиотек классов, я часто вижу такую операцию, напримерEventEmitter3. Создайте объект без прототипа через Object.create(null), даже если вы его установили__proto__
тоже не работает, потому что его прототип изначально нулевой, нет__proro__
изsetter
.
const obj = Object.create(null);
obj.__proto__ = { hack: '污染原型的属性' };
const obj1 = {};
console.log(obj1.__proto__); // => {}
Object.freeze(obj)
Объект obj можно заморозить с помощью Object.freeze(obj).Замороженный объект не может быть изменен и становится нерасширяемым объектом. Как упоминалось ранее, прототип нерасширяемого объекта нельзя изменить, и будет выброшено исключение TypeError:
const obj = Object.freeze({ name: 'xiaoHong' });
obj.xxx = 666;
console.log(obj); // => { name: 'xiaoHong' }
console.log(Object.isExtensible(obj)); // => false
obj.__proto__ = null; // => TypeError: #<Object> is not extensible
Прошло почти три месяца с тех пор, как я ушла из компании, где была стажером, каждый раз вспоминаюnpm install
Все показывают десятки обнаруженных уязвимостей зависимостей. Давно наверное без апгрейда накопилось столько лазеек.Во всяком случае не рискну апгрейдиться невзначай.Прошлый баг проверялся давно и результат вызван апгрейдом аксиоса. Не знаю, обновился ли он еще.
Вопрос интервью, связанный с прототипом, с которым мы столкнулись на недавнем собеседовании при приеме на работу в школу.
Недавно я столкнулся со следующим вопросом на собеседовании на крупной фабрике:
function Page() {
return this.hosts;
}
Page.hosts = ['h1'];
Page.prototype.hosts = ['h2'];
const p1 = new Page();
const p2 = Page();
console.log(p1.hosts);
console.log(p2.hosts);
Результат операции: вывод первыйundefiend
, а затем сообщить об ошибкеTypeError: Cannot read property 'hosts' of undefined
.
Зачемconsole.log(p1.hosts)
это выходundefiend
Что ж, когда мы упоминали new ранее, если мы возвращаем объект, мы будем напрямую использовать этот объект как результат new.p1
должно бытьthis.hosts
результат, покаnew Page()
, этоPage.prototype
прототипtarget
Объект, так что здесьthis.hosts
может получить доступ кPage.prototype.hosts
то есть['h2']
. такp1
равно['h2']
,['h2']
нетhosts
свойство так возвращаетundefined
.
Зачемconsole.log(p2.hosts)
сообщит об ошибке,p2
это прямой вызовPage
Результат конструктора, вызываемого напрямуюpage
функция, на этот разthis
Указывает на глобальный объект, глобальный объект неhosts
собственность, поэтому верниundefined
,Прошлоеundefined
посетитьhosts
Конечно, это неправильно.
Использованная литература:
Эта статья представляет собой оригинальный контент, впервые опубликованный наличный блог, Пожалуйста, укажите источник.