Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
[[Class]] можно использовать особенность работы Object.prototype.toString и написать вот такой кодvar allowedTypes = ["Object", "Undefined", "Null", "Boolean", "Number", "String"];
/**
* @param {Mixed} x
*
* @return {String}
*/
function Type(x) {
var type = Object.prototype.toString.call(x).replace(/^\[object\s|\]$/g, '');
return ~allowedTypes.indexOf(type) && type || "Object";
}
() => из ES6new Array.prototype.slice
// TypeError: Array.prototype.slice is not a constructor
// VS
var A = function () {};
Object.getOwnPropertyDescriptor(A, "prototype");
// {"configurable":false,"enumerable":false,"value":{},"writable":true}
// Убить prototype у пользовательской функции нельзя
delete A.prototype; // false - "configurable":false
new A(); // ok
Array.prototype.slice.prototype={}
new Array.prototype.slice // TypeError: function slice() { [native code] } is not a constructor
var x = 1000/0;
console.log(typeof x); // number
console.log(typeof 1000/0) // NaN
-0 == +0 Division of a non-zero finite value by a zero results in a signed infinity. The sign is determined by the rule already stated above.
1000/0 - Infinity, 0/0 - NaN, NaN !== NaN Это все IEEE 754console.log((1e+16 + 1) == 1e+16); //true
console.log((1e+16 + 100) == 1e+16); //false
Для получения реального «undefined»-значения можно использовать оператор void (кстати, я не знаю другого применения этому оператору):
void 0 == undefined
void function(global) {
//closure
} (this);
var a = {};
a['pewpew'] = 1;
a['ololo'] = 2;
a[1] = 3;
a[0] = 4;
a[1e+9] = 5;
a[1e+10] = 6;
JSON.stringify(a);
JSON.stringify(a);
// V8 (Chrome, Node,js) и Опера
// {"0":4,"1":3,"1000000000":5,"pewpew":1,"ololo":2,"10000000000":6}
// FireFox и Safari
// {"pewpew":1,"ololo":2,"1":3,"0":4,"1000000000":5,"10000000000":6}
null приводится к нулю. null+1===1 Это не то поведение, которого ждёшь от null после СУБД. Зачем нужен такой null, непонятно — вместо него кажется правильным использовать undefined.undefined неперезаписываемое, согласно спецификации ECMAScript 5.[] + [] = ""
[] + {} = "[object Object]"
{} + [] = 0
{} + {} = NaN
//(1) [] + [] = ""
[].toString() // -> "" - Array не является нативным типом - поэтому сериализуется черезе .toString()
"" + "" // -> "" - сложение двух пустых строк равняется пустой строке
//(2) [] + {} = "[object Object]"
{}.toString() // -> "[object Object]" - {} является упрощенной формой new Object()
[].toString() // -> "" - Array не является нативным типом - поэтому сериализуется через .toString()
"" + {} // -> "[object Object]" - сложение строки с объектом невозможно, результат Object
//(3) {} + [] = 0 и {} + {} = NaN
// тут есть одна особенность - результат зависит от движка
// Mozilla Rhino, Opera Presto/2.12.388, Microsoft Trident
{} + [] = 0.0 // действует правило конвертации пустых объектов (нативный тип + ненативный тип), массивы конвертируются в "", объект в 0.
// тоже самое будет если
0 /*{}*/ + "" /*[]*/ = 0.0
{} + {} = NaN // попытка приведения объекта к типу "number" приводит к NaN - что по сумме дает NaN т.к. оба аргумента неприводимы в тип доступный для складывания
// Google V8
{} + [] = "[object Object]"
{} + {} = "[object Object][object Object]"
// Rhino
{} + 1 = 1.0 // {} = 0.0
[] + 1 = 1 // [] = 0
{} + ""; // 0
"" + {}; // "[object Object]"
{;}
+""; // 0
Во втором случае скобки — литерал объекта т.к. стейтмент плюс еще не закрыт.Function.prototype.toString();(function(){{}+""})+"";
/*FF распарсил эту функцию вот так:
"function () {
+ "";
}"*/
/*Опера
"function() {
{}
+""
}"
*/
/*Хром
"function (){{}+""}"
*/
/*Сафари
"function () {{}+"";}
*/
{2+1;} + ""; // 0 - аналогично первому примеру
"" + {2+1;}; // Syntax error - пытается распарсить блок {2+1;} как литерал объекта
var a = {} + ""; // "[object Object]"
оператор typeof используется довольно часто, поэтому лучше запомнить
[] instanceof Array // true
var === undefined
Infinity + 1 == Infinity // true
someVar; // error
window.someVar; // undefined
frame.contentWindow.var instanceof Array;
Object.prototype.toString и получают «Тип» из [[Class]]var === undefined
В JS, всё — свойство какого либо объекта.Вы про какие объекты сейчас говорите? Даю код исключительно для уточнения ваших слов.
function a() {
var a; // свойство какого объекта переменная а?
}
Если запрашивать / Но если запрашиватьтут важно понимание операций Identifier Resolution и Property Accessors, а не их последствий :)
ловить исключения несуществующей переменной в рантаймеОшибку лучше предотвратить, чем обработать исключение.
можно будет поставить прокси на GLOBAL областьВы уверены в понимании идеи Proxy? :) Пример, пожалуйста (Proxy сейчас есть в Firefox).
В JS действительно ВСЕ свойства какого либо объектаЭто я прекрасно понимаю. Хотел уточнить знание автора комента выше, а тут вы :)
сделал библиотеку typeis(«typename»)(object)Из многолетнего опыта — мне всегда хватало typeof и Array.isArray. Зря вы так GC мучаете своими «красивыми» функциональными интерфейсами :)
typeis("typename")(object);
type(object) === "typename"; // faster - true, better IMO
var windowDescriptor = Object.getOwnPropertyDescriptor(this, 'window');
// {"configurable":false,"enumerable":true,"value":window,"writable":false}
this.window = Proxy.create({}); // (А) "writable":false - ничего не получится
window.__noSuchMethod__ = function () {
return function () {};
}
window.b(); // OK __noSuchMethod__ тут сработает
b(); // (B) ReferenceError: b is not defined
(function(){ GLOBAL = Proxy.create({}); })();
» node -v
v0.8.8
» node --harmony_proxies
> GLOBAL = Proxy.create({get: console.log.bind(console)});
util.js:120
var ctx = {
^
RangeError: Maximum call stack size exceeded
» node
> GLOBAL = {a: 1};
{ a: 1 }
> a;
ReferenceError: a is not defined
at repl:1:1
at REPLServer.self.eval (repl.js:111:21)
at rli.on.e (repl.js:260:20)
at REPLServer.self.eval (repl.js:118:5)
at Interface.<anonymous> (repl.js:250:12)
at Interface.EventEmitter.emit (events.js:88:17)
at Interface._onLine (readline.js:199:10)
at Interface._line (readline.js:517:8)
at Interface._ttyWrite (readline.js:735:14)
at ReadStream.onkeypress (readline.js:98:10)
>
console.log.toString() // 'function () {\n process.stdout.write(util.format.apply(this, arguments) + \'\\n\');\n}' где process ссылается на GLOBAL
GLOBAL = {a: 1}
typeof(GLOBAL) // "[object global]"
this.GLOBAL["a"] = 1 // 1
a // 1
Это требование зависит как от кодстайла так и от алгоритма. Однозначано так говорить не хорошо.Не настаиваю, на том что это лучший вариант, это лишь дополнение к instanceof для проверки пустых типов. Хотя в конечном итоге всё сводится к кодстаилу и конкретной ситуации.
Вы про какие объекты сейчас говорите?Да, похоже преувеличил, имея в виду общий случай, в которой дело имеется либо со свойствами объекта «this.var», либо с локальными переменными, объявленными и используемыми в пределах одного экрана, когда нет необходимости помнить была ли переменная объявлена. Реальный случай, когда действительно может остро встать проблема ошибок из-за не объявленных переменных — это десяти экранные функции со множеством переменных, или глобальная область определения, в которой можно воспользоваться «window.var».
тут важно понимание операций Identifier Resolution и Property Accessors, а не их последствий :)Теория и практика — две части целого.
Это баг реализации или это следует из стандарта?Это фича. instanceof использует цепочку прототипов, которая в разых фреймах разная. «Адреса» объектов разные, а функционал одинаковый.
Необходимость кроссфреймовых JS запросов очень редкаНе спорю. Лучше вообще использовать Array.isArray ;-)
Интересно. instanceof только с кроссфреймовыми запросами себя так ведёт, или ещё в каких то ситуациях?
window.Array и unsafeWindow.Array. И внутри приложения используется именно второе. То есть если вы хотите написать плагин — вам придётся использовать unsafeWindow.Array(1,2,3) вместо [1,2,3].{} + {}
{} + []
[] + {}
[] + []
(function(){
var uscope = window, //window['utils'] = (window['utils']?window['utils'] : {}) ,//
types = "Boolean Number String Function Array Date RegExp Object".split(" "),
is = function (type, obj) {
var clas = Object.prototype.toString.call(obj).slice(8, -1);
return obj !== undefined && obj !== null && clas === type;
};
for(var i=0;i<types.length;i++)
uscope['is'+types[i]] = (function( type ){
return function( obj ){return is( type, obj )};
}( types[i] ));
}());
Подводные камни JavaScript