beautify 1st part of the tutorial

This commit is contained in:
Ilya Kantor 2015-03-10 12:36:58 +03:00
parent e3dd2cedc0
commit 6444024a9d
327 changed files with 2358 additions and 1986 deletions

View file

@ -6,7 +6,9 @@
//+ run
var arr = ["a", "b"];
arr.push( function() { alert(this); } )
arr.push(function() {
alert( this );
})
arr[2](); // "a","b",function
```

View file

@ -7,8 +7,10 @@
```js
var arr = ["a", "b"];
arr.push( function() { alert(this); } )
arr.push(function() {
alert( this );
})
arr[2](); // ?
arr[2](); // ?
```

View file

@ -5,10 +5,12 @@
```js
//+ run
var obj = {
go: function() { alert(this) }
go: function() {
alert(this)
}
}
(obj.go)() // error!
(obj.go)() // error!
```
Причем сообщение об ошибке в большинстве браузеров не даёт понять, что на самом деле не так.
@ -18,6 +20,7 @@ var obj = {
JavaScript игнорирует перевод строки перед скобкой `(obj.go)()` и читает этот код как:
```js
//+ no-beautify
var obj = { go:... }(obj.go)()
```

View file

@ -5,6 +5,7 @@
Каков будет результат этого кода?
```js
//+ no-beautify
var obj = {
go: function() { alert(this) }
}

View file

@ -4,6 +4,7 @@
<li>Здесь не просто вызов `obj.method()`, а более сложный вызов вида `(выражение).method()`. Такой вызов работает, как если бы он был разбит на две строки:
```js
//+ no-beautify
f = obj.go; // сначала вычислить выражение
f(); // потом вызвать то, что получилось
```

View file

@ -5,7 +5,7 @@
Вызовы `(1)` и `(2)` в примере ниже работают не так, как `(3)` и `(4)`:
```js
//+ run
//+ run no-beautify
"use strict"
var obj, f;

View file

@ -6,13 +6,13 @@ var name = "";
var user = {
name: "Василий",
*!*
export: this // (*)
*/!*
};
};
alert(user.export.name);
alert( user.export.name );
```
Объявление объекта само по себе не влияет на `this`. Никаких функций, которые могли бы повлиять на контекст, здесь нет.

View file

@ -9,10 +9,10 @@ var name = "";
var user = {
name: "Василий",
export: this
};
alert(user.export.name);
export: this
};
alert( user.export.name );
```

View file

@ -9,13 +9,13 @@ var name = "";
var user = {
name: "Василий",
export: function() {
export: function() {
return this;
}
};
};
alert(user.export().name);
alert( user.export().name );
```

View file

@ -9,15 +9,15 @@ var name = "";
var user = {
name: "Василий",
export: function() {
return {
return {
value: this
};
}
};
};
alert(user.export().value.name);
alert( user.export().value.name );
```

View file

@ -10,8 +10,8 @@
</ul>
```js
var calculator = {
... ваш код...
var calculator = {
...ваш код...
}
calculator.read();

View file

@ -4,20 +4,20 @@
//+ run
var ladder = {
step: 0,
up: function() {
this.step++;
up: function() {
this.step++;
return this;
},
down: function() {
this.step--;
down: function() {
this.step--;
return this;
},
showStep: function() {
alert(this.step);
showStep: function() {
alert( this.step );
return this;
}
}
ladder.up().up().down().up().down().showStep(); // 1
ladder.up().up().down().up().down().showStep(); // 1
```

View file

@ -7,14 +7,14 @@
```js
var ladder = {
step: 0,
up: function() { // вверх по лестнице
up: function() { // вверх по лестнице
this.step++;
},
down: function() { // вниз по лестнице
down: function() { // вниз по лестнице
this.step--;
},
showStep: function() { // вывести текущую ступеньку
alert(this.step);
alert( this.step );
}
};
```
@ -31,7 +31,7 @@ ladder.showStep(); // 1
Модифицируйте код методов объекта, чтобы вызовы можно было делать цепочкой, вот так:
```js
ladder.up().up().down().up().down().showStep(); // 1
ladder.up().up().down().up().down().showStep(); // 1
```
Такой подход называется "чейнинг" (chaining) и используется, например, во фреймворке jQuery.

View file

@ -16,7 +16,7 @@ var user = {
// метод
*/!*
sayHi: function() {
alert('Привет!');
alert( 'Привет!' );
}
};
@ -101,7 +101,7 @@ admin.sayHi(); // упс! внутри sayHi обращение по старо
Через `this` метод может не только обратиться к любому свойству объекта, но и передать куда-то ссылку на сам объект целиком:
```js
//+ run
//+ run no-beautify
var user = {
name: 'Василий',
@ -138,7 +138,7 @@ function sayHi() {
**Если одну и ту же функцию запускать в контексте разных объектов, она будет получать разный `this`:**
```js
//+ run
//+ run no-beautify
var user = { firstName: "Вася" };
var admin = { firstName: "Админ" };
@ -169,8 +169,8 @@ admin['g'](); // Админ (не важно, доступ к объекту ч
```js
//+ run
function func() {
alert(this); // выведет [object Window] или [object global]
function func() {
alert( this ); // выведет [object Window] или [object global]
}
func();
@ -182,9 +182,9 @@ func();
```js
//+ run
function func() {
function func() {
"use strict";
alert(this); // выведет undefined (кроме IE9-)
alert( this ); // выведет undefined (кроме IE9-)
}
func();
@ -199,7 +199,7 @@ func();
Любой более хитрый вызов приведёт к потере контекста, например:
```js
//+ run
//+ run no-beautify
var user = {
name: "Вася",
hi: function() { alert(this.name); },

View file

@ -9,7 +9,7 @@ P.S.
```js
//+ run
alert( ['x','y'] == 'x,y' ); // true
alert( ['x', 'y'] == 'x,y' ); // true
alert( [] == '' ); // true
```

View file

@ -8,17 +8,17 @@
```js
var foo = {
toString: function () {
return 'foo';
},
valueOf: function () {
return 2;
}
toString: function() {
return 'foo';
},
valueOf: function() {
return 2;
}
};
alert(foo);
alert(foo + 1);
alert(foo + "3");
alert( foo );
alert( foo + 1 );
alert( foo + "3" );
```
Подумайте, прежде чем ответить.

View file

@ -6,7 +6,7 @@
```js
//+ run
alert( [] == [] ); // false
alert( [] == [] ); // false
alert( [] == ![] ); // true
```

View file

@ -1,6 +1,7 @@
```js
//+ no-beautify
new Date(0) - 0 = 0 // (1)
new Array(1)[0] + "" = "undefined" // (2)
({})[0] = undefined // (3)
@ -24,6 +25,7 @@ new Array(1)[0] + "" = "undefined" // (2)
Если это непонятно, то посмотрите на такой пример:
```js
//+ no-beautify
alert( [1,[0],2][1] );
```

View file

@ -5,6 +5,7 @@
Подумайте, какой результат будет у выражений ниже. Когда закончите -- сверьтесь с решением.
```js
//+ no-beautify
new Date(0) - 0
new Array(1)[0] + ""
({})[0]

View file

@ -13,23 +13,25 @@
```js
//+ run
function sum(a) {
var currentSum = a;
function f(b) {
currentSum += b;
return f;
}
f.toString = function() { return currentSum; };
f.toString = function() {
return currentSum;
};
return f;
}
alert( sum(1)(2) ); // 3
alert( sum(5)(-1)(2) ); // 6
alert( sum(6)(-1)(-2)(-3) ); // 0
alert( sum(0)(1)(2)(3)(4)(5) ); // 15
alert( sum(1)(2) ); // 3
alert( sum(5)(-1)(2) ); // 6
alert( sum(6)(-1)(-2)(-3) ); // 0
alert( sum(0)(1)(2)(3)(4)(5) ); // 15
```
При внимательном взгляде на решение легко заметить, что функция `sum` срабатывает только один раз. Она возвращает функцию `f`.

View file

@ -22,8 +22,8 @@
```js
//+ run
if ( {} && [] ) {
alert("Все объекты - true!"); // alert сработает
if ({} && []) {
alert( "Все объекты - true!" ); // alert сработает
}
```
@ -33,11 +33,11 @@ if ( {} && [] ) {
```js
//+ run
var user = {
firstName: 'Василий'
var user = {
firstName: 'Василий'
};
alert(user); // [object Object]
alert( user ); // [object Object]
```
Как видно, содержимое объекта не вывелось. Это потому, что стандартным строковым представлением пользовательского объекта является строка `"[object Object]"`.
@ -68,10 +68,12 @@ alert( user ); // Пользователь Василий
```js
//+ run
var obj = {
toString: function() { return 123; }
toString: function() {
return 123;
}
};
alert(obj); // 123
alert( obj ); // 123
```
Поэтому мы и называем его здесь *"строковое преобразование"*, а не "преобразование к строке".
@ -81,9 +83,9 @@ alert(obj); // 123
```js
//+ run
alert( [1,2] ); // toString для массивов выводит список элементов "1,2"
alert( [1, 2] ); // toString для массивов выводит список элементов "1,2"
alert( new Date ); // toString для дат выводит дату в виде строки
alert( function() { } ); // toString для функции выводит её код
alert( function() {} ); // toString для функции выводит её код
```
## Численное преобразование
@ -134,11 +136,13 @@ alert( +new Date() ); // valueOf: кол-во миллисекунд, проше
```js
//+ run
var obj = {
valueOf: function() { return 1; }
var obj = {
valueOf: function() {
return 1;
}
};
alert(obj == true); // true
alert( obj == true ); // true
```
Объект `obj` был сначала преобразован в примитив, используя численное преобразование, получилось `1 == true`.
@ -149,25 +153,31 @@ alert(obj == true); // true
```js
//+ run
var obj = {
valueOf: function() { return 1; }
var obj = {
valueOf: function() {
return 1;
}
};
alert(obj + "test"); // 1test
alert( obj + "test" ); // 1test
```
Или вот, для разности объектов:
```js
//+ run
var a = {
valueOf: function() { return "1"; }
var a = {
valueOf: function() {
return "1";
}
};
var b = {
valueOf: function() { return "2"; }
var b = {
valueOf: function() {
return "2";
}
};
alert(a - b); // "1" - "2" = -1
alert( a - b ); // "1" - "2" = -1
```
[warn header="Исключение: `Date`"]
@ -194,9 +204,9 @@ alert( +new Date ); // число миллисекунд
```js
//+ run
var value = new Boolean(false);
if ( value ) {
alert(true); // сработает!
var value = new Boolean(false);
if (value) {
alert( true ); // сработает!
}
```
@ -204,14 +214,14 @@ if ( value ) {
```js
//+ run
var value = new Boolean(false);
var value = new Boolean(false);
*!*
alert(value); // выводит false, все ок..
alert( value ); // выводит false, все ок..
*/!*
if ( value ) {
alert(true); // ..но тогда почему выполняется alert в if ?!?
if (value) {
alert( true ); // ..но тогда почему выполняется alert в if ?!?
}
```
@ -236,6 +246,7 @@ if ( value ) {
Заметим, для полноты картины, что некоторые тесты знаний в интернет предлагают вопросы типа:
```js
//+ no-beautify
{}[0] // чему равно?
{} + {} // а так?
```
@ -252,6 +263,7 @@ if ( value ) {
А если команду изъять, то будет пустой блок `{}`, который ничего не делает. Два примера выше как раз содержат пустой блок в начале, который ничего не делает. Иначе говоря:
```js
//+ no-beautify
{}[0] // то же что и: [0]
{} + {} // то же что и: + {}
```

View file

@ -5,7 +5,7 @@
Например, они могут вернуть один и тот же объект `obj`, определённый снаружи:
```js
//+ run
//+ run no-beautify
var obj = {};
function A() { return obj; }

View file

@ -5,6 +5,7 @@
Возможны ли такие функции `A` и `B` в примере ниже, что соответствующие объекты `a,b` равны (см. код ниже)?
```js
//+ no-beautify
function A() { ... }
function B() { ... }

View file

@ -5,7 +5,7 @@
function Calculator() {
this.read = function() {
this.a = +prompt('a?', 0);
this.a = +prompt('a?', 0);
this.b = +prompt('b?', 0);
};

View file

@ -20,7 +20,7 @@ function Calculator() {
op = split[1],
b = +split[2]
if(!methods[op] || isNaN(a) || isNaN(b)) {
if (!methods[op] || isNaN(a) || isNaN(b)) {
return NaN;
}
@ -45,7 +45,7 @@ calc.addMethod("**", function(a, b) {
});
var result = calc.calculate("2 ** 3");
alert(result); // 8
alert( result ); // 8
```
<ul>

View file

@ -13,7 +13,7 @@
```js
var calc = new Calculator;
alert(calc.calculate("3 + 7")); // 10
alert( calc.calculate("3 + 7") ); // 10
```
</li>
@ -23,12 +23,18 @@ alert(calc.calculate("3 + 7")); // 10
```js
var powerCalc = new Calculator;
powerCalc.addMethod("*", function(a, b) { return a * b; });
powerCalc.addMethod("/", function(a, b) { return a / b; });
powerCalc.addMethod("**", function(a, b) { return Math.pow(a, b); });
powerCalc.addMethod("*", function(a, b) {
return a * b;
});
powerCalc.addMethod("/", function(a, b) {
return a / b;
});
powerCalc.addMethod("**", function(a, b) {
return Math.pow(a, b);
});
var result = powerCalc.calculate("2 ** 3");
alert(result); // 8
alert( result ); // 8
```
</li>

View file

@ -13,7 +13,7 @@
```js
function Animal(name) {
this.name = name;
this.canWalk = true;
this.canWalk = true;
}
*!*
@ -52,7 +52,7 @@ function Animal(name) {
// в this пишем свойства, методы
this.name = name;
this.canWalk = true;
this.canWalk = true;
*!*
// return this
@ -75,7 +75,7 @@ function Animal(name) {
Например, возврат объекта:
```js
//+ run
//+ run no-beautify
function BigAnimal() {
this.name = "Мышь";
@ -97,7 +97,7 @@ function BigAnimal() {
return "Годзилла"; // <-- возвратим примитив
}
alert( new BigAnimal().name ); // Мышь, получили this (а Годзилла пропал)
alert( new BigAnimal().name ); // Мышь, получили this (а Годзилла пропал)
```
Эта особенность работы `new` прописана в стандарте, но используется она весьма редко.
@ -126,9 +126,9 @@ var animal = new BigAnimal();
//+ run
function User(name) {
this.name = name;
this.sayHi = function() {
alert("Моё имя: " + this.name);
alert( "Моё имя: " + this.name );
};
}
@ -156,15 +156,15 @@ function User(firstName, lastName) {
*!*
// вспомогательная переменная
var phrase = "Привет";
// вспомогательная вложенная функция
function getFullName() {
return firstName + " " + lastName;
}
function getFullName() {
return firstName + " " + lastName;
}
*/!*
this.sayHi = function() {
alert(phrase + ", " + getFullName()); // использование
alert( phrase + ", " + getFullName() ); // использование
};
}

View file

@ -1,7 +1,6 @@
```js
//+ run
function User(fullName) {
this.fullName = fullName;
@ -38,12 +37,12 @@ function User(fullName) {
var vasya = new User("Василий Попкин");
// чтение firstName/lastName
alert(vasya.firstName); // Василий
alert(vasya.lastName); // Попкин
alert( vasya.firstName ); // Василий
alert( vasya.lastName ); // Попкин
// запись в lastName
vasya.lastName = 'Сидоров';
alert(vasya.fullName); // Василий Сидоров
alert( vasya.fullName ); // Василий Сидоров
```

View file

@ -20,13 +20,13 @@ var vasya = new User("Василий Попкин");
var vasya = new User("Василий Попкин");
// чтение firstName/lastName
alert(vasya.firstName); // Василий
alert(vasya.lastName); // Попкин
alert( vasya.firstName ); // Василий
alert( vasya.lastName ); // Попкин
// запись в lastName
vasya.lastName = 'Сидоров';
alert(vasya.fullName); // Василий Сидоров
alert( vasya.fullName ); // Василий Сидоров
```
Важно: не рекомендуется дублировать одни и те же данные в различных свойствах. Поэтому в этой задаче `fullName` должно остаться свойством, а `firstName/lastName` -- реализованы через `get/set`.

View file

@ -46,6 +46,7 @@ Object.defineProperty(obj, prop, descriptor)
Два таких вызова работают одинаково:
```js
//+ no-beautify
var user = {};
// 1. простое присваивание
@ -69,7 +70,7 @@ var user = {};
Object.defineProperty(user, "name", {
value: "Вася",
writable: false, // запретить присвоение "user.name="
writable: false, // запретить присвоение "user.name="
configurable: false // запретить удаление "delete user.name"
});
@ -90,7 +91,7 @@ user.name = "Петя";
К сожалению, свойство `toString`, объявленное обычным способом, будет видно в цикле `for..in`, например:
```js
//+ run
//+ run no-beautify
var user = {
name: "Вася",
toString: function() { return this.name; }
@ -106,7 +107,7 @@ for(var key in user) alert(key); // name, toString
`Object.defineProperty` может исключить `toString` из списка итерации, поставив ему флаг `enumerable: false`. По стандарту, у встроенного `toString` этот флаг уже стоит.
```js
//+ run
//+ run no-beautify
var user = {
name: "Вася",
toString: function() { return this.name; }
@ -169,18 +170,18 @@ Object.defineProperty(user, "fullName", {
*!*
set: function(value) {
var split = value.split(' ');
this.firstName = split[0];
this.surname = split[1];
}
var split = value.split(' ');
this.firstName = split[0];
this.surname = split[1];
}
*/!*
});
*!*
user.fullName = "Петя Иванов";
*/!*
alert(user.firstName); // Петя
alert(user.surname); // Иванов
alert( user.firstName ); // Петя
alert( user.surname ); // Иванов
```
## Указание get/set в литералах
@ -213,11 +214,11 @@ var user = {
};
*!*
alert(user.fullName); // Вася Петров (из геттера)
alert( user.fullName ); // Вася Петров (из геттера)
user.fullName = "Петя Иванов";
alert(user.firstName); // Петя (поставил сеттер)
alert(user.surname); // Иванов (поставил сеттер)
alert( user.firstName ); // Петя (поставил сеттер)
alert( user.surname ); // Иванов (поставил сеттер)
*/!*
```
@ -237,7 +238,7 @@ function User(name, age) {
var pete = new User("Петя", 25);
alert(pete.age); // 25
alert( pete.age ); // 25
```
С обычными свойствами в коде меньше букв, они удобны, причины использовать функции пока нет.
@ -268,8 +269,8 @@ function User(name, birthday) {
this.birthday = birthday;
*!*
Object.defineProperty(this, "age", {
get: function() {
Object.defineProperty(this, "age", {
get: function() {
var todayYear = new Date().getFullYear();
return todayYear - this.birthday.getFullYear();
}
@ -279,7 +280,7 @@ function User(name, birthday) {
var pete = new User("Петя", new Date(1987, 6, 1));
alert(pete.age); // получает возраст из даты рождения
alert( pete.age ); // получает возраст из даты рождения
```
Таким образом, `defineProperty` позволяет нам использовать обычные свойства и, при необходимости, в любой момент заменить их на функции, сохраняя полную совместимость.
@ -335,7 +336,9 @@ var obj = {
internal: 3
};
Object.defineProperty(obj, "internal", {enumerable: false});
Object.defineProperty(obj, "internal", {
enumerable: false
});
*!*
alert( Object.keys(obj) ); // a,b
@ -351,7 +354,9 @@ alert( Object.getOwnPropertyNames(obj) ); // a, internal, b
```js
//+ run
var obj = { test: 5 };
var obj = {
test: 5
};
*!*
var descriptor = Object.getOwnPropertyDescriptor(obj, 'test');
*/!*
@ -362,7 +367,7 @@ var descriptor = Object.getOwnPropertyDescriptor(obj, 'test');
delete descriptor.value; // ..нужно убрать value/writable
delete descriptor.writable;
descriptor.get = function() { // и поставить get
alert("Preved :)");
alert( "Preved :)" );
};
*!*
@ -370,7 +375,7 @@ descriptor.get = function() { // и поставить get
*/!*
// если не удалить - defineProperty объединит старый дескриптор с новым
delete obj.test;
delete obj.test;
Object.defineProperty(obj, 'test', descriptor);

View file

@ -2,7 +2,7 @@
```js
//+ run
function Article() {
function Article() {
this.created = new Date;
*!*
@ -14,7 +14,7 @@ Article.count = 0; // начальное значение
// (нельзя оставить undefined, т.к. Article.count++ будет NaN)
Article.showStats = function() {
alert('Всего: ' + this.count + ', Последняя: ' + this.last);
alert( 'Всего: ' + this.count + ', Последняя: ' + this.last );
};
new Article();

View file

@ -38,7 +38,7 @@ Article.count = 0;
Article.showCount = function() {
*!*
alert(this.count); // (1)
alert( this.count ); // (1)
*/!*
}
@ -82,10 +82,10 @@ function Journal(date) {
this.date = date;
this.formatDate = function(date) {
return date.getDate() + '.' + (date.getMonth()+1) + '.' + date.getFullYear();
return date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear();
};
this.getTitle = function() {
this.getTitle = function() {
return "Выпуск от " + this.formatDate(this.date);
};
@ -99,17 +99,17 @@ Journal.compare = function(journalA, journalB) {
// использование:
var journals = [
new Journal(new Date(2012,1,1)),
new Journal(new Date(2012,0,1)),
new Journal(new Date(2011,11,1))
new Journal(new Date(2012, 1, 1)),
new Journal(new Date(2012, 0, 1)),
new Journal(new Date(2011, 11, 1))
];
function findMin(journals) {
var min = 0;
for(var i=0; i<journals.length; i++) {
for (var i = 0; i < journals.length; i++) {
*!*
// используем статический метод
if ( Journal.compare(journals[min], journals[i]) > 0 ) min = i;
if (Journal.compare(journals[min], journals[i]) > 0) min = i;
*/!*
}
return journals[min];
@ -154,7 +154,7 @@ alert( *!*Journal.formatDate(new Date)*/!* );
```js
//+ run
var str = String.fromCharCode(65);
alert(str); // 'A'
alert( str ); // 'A'
```
Но строки -- слишком простой пример, посмотрим что-нибудь посложнее.
@ -165,15 +165,17 @@ alert(str); // 'A'
```js
//+ run
function User(userData) {
function User(userData) {
if (userData) { // если указаны данные -- одна ветка if
this.name = userData.name;
this.age = userData.age;
} else { // если не указаны -- другая
} else { // если не указаны -- другая
this.name = 'Аноним';
}
this.sayHi = function() { alert(this.name) };
this.sayHi = function() {
alert(this.name)
};
// ...
}
@ -182,7 +184,10 @@ function User(userData) {
var guest = new User();
guest.sayHi(); // Аноним
var knownUser = new User({name: 'Вася', age: 25});
var knownUser = new User({
name: 'Вася',
age: 25
});
knownUser.sayHi(); // Вася
```
@ -192,8 +197,10 @@ knownUser.sayHi(); // Вася
```js
//+ run
function User() {
this.sayHi = function() { alert(this.name) };
function User() {
this.sayHi = function() {
alert(this.name)
};
}
User.createAnonymous = function() {
@ -215,7 +222,10 @@ User.createFromData = function(userData) {
var guest = User.createAnonymous();
guest.sayHi(); // Аноним
var knownUser = User.createFromData({name: 'Вася', age: 25});
var knownUser = User.createFromData({
name: 'Вася',
age: 25
});
knownUser.sayHi(); // Вася
*/!*
```

View file

@ -5,12 +5,12 @@
function sumArgs() {
// скопируем reduce из массива
arguments.reduce = [].reduce;
return arguments.reduce(function(a, b) {
return arguments.reduce(function(a, b) {
return a + b;
});
}
alert( sumArgs(4,5,6) ); // 15
alert( sumArgs(4, 5, 6) ); // 15
```
# Второй вариант
@ -21,11 +21,11 @@ alert( sumArgs(4,5,6) ); // 15
//+ run
function sumArgs() {
// запустим reduce из массива напрямую
return [].reduce.call(arguments, function(a, b) {
return a + b;
return [].reduce.call(arguments, function(a, b) {
return a + b;
});
}
alert( sumArgs(4,5,6) ); // 15
alert( sumArgs(4, 5, 6) ); // 15
```

View file

@ -7,10 +7,12 @@
```js
//+ run
function sum(arr) {
return arr.reduce(function(a, b) { return a + b; });
return arr.reduce(function(a, b) {
return a + b;
});
}
alert( sum([1,2,3]) ); // 6 (=1+2+3)
alert( sum([1, 2, 3]) ); // 6 (=1+2+3)
```
Создайте аналогичную функцию `sumArgs()`, которая будет суммировать все свои аргументы:
@ -20,7 +22,7 @@ function sumArgs() {
/* ваш код */
}
alert( sumArgs(1,2,3) ); // 6, аргументы переданы через запятую, без массива
alert( sumArgs(1, 2, 3) ); // 6, аргументы переданы через запятую, без массива
```
Для решения примените метод `reduce` к `arguments`, используя `call`, `apply` или одалживание метода.

View file

@ -3,16 +3,21 @@
```js
//+ run
function sum() {
return [].reduce.call(arguments, function(a, b) { return a + b; });
return [].reduce.call(arguments, function(a, b) {
return a + b;
});
}
function mul() {
return [].reduce.call(arguments, function(a, b) { return a * b; });
return [].reduce.call(arguments, function(a, b) {
return a * b;
});
}
*!*
function applyAll(func) {
return func.apply(this, [].slice.call(arguments, 1) );
}
return func.apply(this, [].slice.call(arguments, 1));
}
*/!*
alert( applyAll(sum, 1, 2, 3) ); // 6

View file

@ -21,10 +21,15 @@ alert( applyAll(Math.min, 2, -2, 3) ); // -2
```js
//+ run
function sum() { // суммирует аргументы: sum(1,2,3) = 6
return [].reduce.call(arguments, function(a, b) { return a + b; });
return [].reduce.call(arguments, function(a, b) {
return a + b;
});
}
function mul() { // перемножает аргументы: mul(2,3,4) = 24
return [].reduce.call(arguments, function(a, b) { return a * b; });
return [].reduce.call(arguments, function(a, b) {
return a * b;
});
}
*!*

View file

@ -11,7 +11,7 @@
Синтаксис метода `call`:
```js
func.call(context, arg1, arg2,...)
func.call(context, arg1, arg2, ...)
```
При этом вызывается функция `func`, первый аргумент `call` становится её `this`, а остальные передаются "как есть".
@ -21,7 +21,7 @@ func.call(context, arg1, arg2,...)
Например, у нас есть функция `showFullName`, которая работает с `this`:
```js
function showFullName() {
function showFullName() {
alert( this.firstName + " " + this.lastName );
}
```
@ -32,18 +32,18 @@ function showFullName() {
```js
//+ run
function showFullName() {
function showFullName() {
alert( this.firstName + " " + this.lastName );
}
var user = {
var user = {
firstName: "Василий",
lastName: "Петров"
};
*!*
// функция вызовется с this=user
showFullName.call(user) // "Василий Петров"
showFullName.call(user) // "Василий Петров"
*/!*
```
@ -51,20 +51,20 @@ showFullName.call(user) // "Василий Петров"
```js
//+ run
var user = {
var user = {
firstName: "Василий",
surname: "Петров",
patronym: "Иванович"
};
function showFullName(firstPart, lastPart) {
function showFullName(firstPart, lastPart) {
alert( this[firstPart] + " " + this[lastPart] );
}
*!*
// f.call(контекст, аргумент1, аргумент2, ...)
showFullName.call(user, 'firstName', 'surname') // "Василий Петров"
showFullName.call(user, 'firstName', 'patronym') // "Василий Иванович"
showFullName.call(user, 'firstName', 'surname') // "Василий Петров"
showFullName.call(user, 'firstName', 'patronym') // "Василий Иванович"
*/!*
```
@ -85,9 +85,9 @@ showFullName.call(user, 'firstName', 'patronym') // "Василий Ивано
function printArgs() {
arguments.join = [].join; // одолжили метод (1)
var argStr = arguments.join(':'); // (2)
var argStr = arguments.join(':'); // (2)
alert(argStr); // сработает и выведет 1:2:3
alert( argStr ); // сработает и выведет 1:2:3
}
printArgs(1, 2, 3);
@ -107,12 +107,12 @@ printArgs(1, 2, 3);
function join(separator) {
if (!this.length) return '';
var str = this[0];
var str = this[0];
for (var i = 1; i<this.length; i++) {
str += separator + this[i];
for (var i = 1; i < this.length; i++) {
str += separator + this[i];
}
return str;
}
```
@ -123,9 +123,9 @@ function join(separator) {
```js
//+ run
var obj = { // обычный объект с числовыми индексами и length
0: "А",
1: "Б",
var obj = { // обычный объект с числовыми индексами и length
0: "А",
1: "Б",
2: "В",
length: 3
};
@ -154,10 +154,10 @@ function printArgs() {
*!*
// вызовем join с this=arguments,
// этот вызов эквивалентен arguments.join(':') из примера выше
var argStr = join.call(arguments, ':');
var argStr = join.call(arguments, ':');
*/!*
alert(argStr); // сработает и выведет 1:2:3
alert( argStr ); // сработает и выведет 1:2:3
}
printArgs(1, 2, 3);
@ -195,7 +195,7 @@ printArgs('Привет', 'мой', 'мир'); // Привет, мой, мир
**Вызов функции при помощи `func.apply` работает аналогично `func.call`, но принимает массив аргументов вместо списка.**
```js
func.call(context, arg1, arg2)
func.call(context, arg1, arg2);
// идентичен вызову
func.apply(context, [arg1, arg2]);
```
@ -203,7 +203,7 @@ func.apply(context, [arg1, arg2]);
В частности, эти две строчки cработают одинаково:
```js
showFullName.call(user, 'firstName', 'surname');
showFullName.call(user, 'firstName', 'surname');
showFullName.apply(user, ['firstName', 'surname']);
```
@ -243,10 +243,10 @@ alert( Math.max.apply(null, arr) ); // 5
Современный стандарт:
```js
//+ run
function f() {
function f() {
"use strict";
*!*
alert(this); // null
alert( this ); // null
*/!*
}
@ -257,11 +257,11 @@ f.call(null);
```js
//+ run
function f() {
alert(this); // window
function f() {
alert( this ); // window
}
f.call(null);
f.call(null);
```
[/smart]
@ -275,6 +275,7 @@ f.call(null);
<dd>
```js
//+ no-beautify
obj.func(...) // this = obj
obj["func"](...)
```
@ -284,7 +285,7 @@ obj["func"](...)
<dd>
```js
func(...) // this = window (ES3) /undefined (ES5)
func(...) // this = window (ES3) /undefined (ES5)
```
</dd>
@ -292,7 +293,7 @@ func(...) // this = window (ES3) /undefined (ES5)
<dd>
```js
new func() // this = {} (новый объект)
new func() // this = {} (новый объект)
```
</dd>

View file

@ -5,6 +5,7 @@
Если вы вдруг захотите копнуть поглубже -- аналог `bind` для IE8- и старых версий других браузеров будет выглядеть следующим образом:
```js
//+ no-beautify
function bind(func, context /*, args*/) {
var bindArgs = [].slice.call(arguments, 2); // (1)
function wrapper() { // (2)

View file

@ -9,7 +9,7 @@ function f() {
var user = {
g: f.bind("Hello")
}
user.g();
```

View file

@ -12,7 +12,7 @@ function f() {
var user = {
g: f.bind("Hello")
}
user.g();
```

View file

@ -1,7 +1,7 @@
Ответ: `"Вася"`.
```js
//+ run
//+ run no-beautify
function f() {
alert(this.name);
}
@ -19,8 +19,8 @@ f(); // Вася
```js
function bind(func, context) {
return function() {
return func.apply(context, arguments);
return function() {
return func.apply(context, arguments);
};
}
```
@ -28,6 +28,7 @@ function bind(func, context) {
Код станет таким:
```js
//+ no-beautify
function f() {
alert(this.name);
}
@ -43,8 +44,8 @@ f(); // Вася
```js
function bind(func, context) {
*!*
return function() {
return func.apply(context, arguments);
return function() {
return func.apply(context, arguments);
};
*/!*
}

View file

@ -5,6 +5,7 @@
Что выведет этот код?
```js
//+ no-beautify
function f() {
alert(this.name);
}

View file

@ -5,16 +5,18 @@
В свойство функции записано значение. Изменится ли оно после применения `bind`? Обоснуйте ответ.
```js
function sayHi() {
alert(this.name);
function sayHi() {
alert( this.name );
}
sayHi.test = 5;
alert(sayHi.test); // 5
alert( sayHi.test ); // 5
*!*
var bound = sayHi.bind({ name: "Вася" });
var bound = sayHi.bind({
name: "Вася"
});
alert(bound.test); // что выведет? почему?
alert( bound.test ); // что выведет? почему?
*/!*
```

View file

@ -19,11 +19,11 @@ var user = {
password: '12345',
loginOk: function() {
alert(this.login + ' вошёл в сайт');
alert( this.login + ' вошёл в сайт' );
},
loginFail: function() {
alert(this.login + ': ошибка входа');
alert( this.login + ': ошибка входа' );
},
checkPassword: function() {
@ -43,6 +43,7 @@ vasya.checkPassword();
Альтернативное решение -- сделать функции-обёртки над `user.loginOk/loginFail`:
```js
//+ no-beautify
var user = {
...
checkPassword: function() {
@ -79,19 +80,23 @@ var user = {
password: '12345',
loginOk: function() {
alert(this.login + ' вошёл в сайт');
alert( this.login + ' вошёл в сайт' );
},
loginFail: function() {
alert(this.login + ': ошибка входа');
alert( this.login + ': ошибка входа' );
},
checkPassword: function() {
*!*
var self = this;
ask("Ваш пароль?", this.password,
function() { self.loginOk(); },
function() { self.loginFail(); }
ask("Ваш пароль?", this.password,
function() {
self.loginOk();
},
function() {
self.loginFail();
}
);
*/!*
}

View file

@ -23,11 +23,11 @@ var user = {
password: '12345',
loginOk: function() {
alert(this.login + ' вошёл в сайт');
alert( this.login + ' вошёл в сайт' );
},
loginFail: function() {
alert(this.login + ': ошибка входа');
alert( this.login + ': ошибка входа' );
},
checkPassword: function() {

View file

@ -17,7 +17,7 @@ var user = {
password: '12345',
loginDone: function(result) {
alert(this.login + (result ? ' вошёл в сайт' : ' ошибка входа'));
alert( this.login + (result ? ' вошёл в сайт' : ' ошибка входа') );
},
checkPassword: function() {
@ -49,15 +49,19 @@ var user = {
password: '12345',
loginDone: function(result) {
alert(this.login + (result ? ' вошёл в сайт' : ' ошибка входа'));
alert( this.login + (result ? ' вошёл в сайт' : ' ошибка входа') );
},
checkPassword: function() {
var self = this;
*!*
ask("Ваш пароль?", this.password,
function() { self.loginDone(true); },
function() { self.loginDone(false); }
function() {
self.loginDone(true);
},
function() {
self.loginDone(false);
}
);
*/!*
}

View file

@ -30,14 +30,18 @@ var user = {
// метод для вызова из ask
loginDone: function(result) {
alert(this.login + (result ? ' вошёл в сайт' : ' ошибка входа'));
alert( this.login + (result ? ' вошёл в сайт' : ' ошибка входа') );
},
checkPassword: function() {
*!*
ask("Ваш пароль?", this.password,
function() { user.loginDone(true); },
function() { user.loginDone(false); }
ask("Ваш пароль?", this.password,
function() {
user.loginDone(true);
},
function() {
user.loginDone(false);
}
);
*/!*
}

View file

@ -19,7 +19,7 @@
```js
//+ run
setTimeout(function() {
alert("Привет");
alert( "Привет" );
}, 1000);
```
@ -29,13 +29,13 @@ setTimeout(function() {
//+ run
var user = {
firstName: "Вася",
sayHi: function() {
alert(this.firstName);
sayHi: function() {
alert( this.firstName );
}
};
*!*
setTimeout( user.sayHi, 1000); // undefined (не Вася!)
setTimeout(user.sayHi, 1000); // undefined (не Вася!)
*/!*
```
@ -61,15 +61,15 @@ setTimeout(f, 1000); // контекст user потеряли
//+ run
var user = {
firstName: "Вася",
sayHi: function() {
alert(this.firstName);
sayHi: function() {
alert( this.firstName );
}
};
*!*
setTimeout(function() {
user.sayHi(); // Вася
}, 1000);
user.sayHi(); // Вася
}, 1000);
*/!*
```
@ -82,15 +82,15 @@ setTimeout(function() {
//+ run
var user = {
firstName: "Вася",
sayHi: function(who) {
alert(this.firstName + ": Привет, " + who);
sayHi: function(who) {
alert( this.firstName + ": Привет, " + who );
}
};
*!*
setTimeout(function() {
user.sayHi("Петя"); // Вася: Привет, Петя
}, 1000);
user.sayHi("Петя"); // Вася: Привет, Петя
}, 1000);
*/!*
```
@ -108,7 +108,7 @@ setTimeout(function() {
```js
function bind(func, context) {
return function() { // (*)
return func.apply(context, arguments);
return func.apply(context, arguments);
};
}
```
@ -117,10 +117,12 @@ function bind(func, context) {
```js
//+ run
function f() { alert(this); }
function f() {
alert( this );
}
var g = bind(f, "Context");
g(); // Context
g(); // Context
```
То есть, `bind(f, "Context")` привязывает `"Context"` в качестве `this` для `f`.
@ -132,16 +134,16 @@ g(); // Context
Вот она отдельно:
```js
function() { // (*)
return func.apply(context, arguments);
function() { // (*)
return func.apply(context, arguments);
};
```
Если подставить наши конкретные аргументы, то есть `f` и `"Context"`, то получится так:
```js
function() { // (*)
return f.apply("Context", arguments);
function() { // (*)
return f.apply("Context", arguments);
};
```
@ -153,13 +155,13 @@ function() { // (*)
```js
//+ run
function f(a, b) {
alert(this);
alert(a + b);
function f(a, b) {
alert( this );
alert( a + b );
}
var g = bind(f, "Context");
g(1, 2); // Context, затем 3
g(1, 2); // Context, затем 3
```
Аргументы, которые получила `g(...)`, передаются в `f` также благодаря методу `.apply`.
@ -171,20 +173,20 @@ g(1, 2); // Context, затем 3
```js
//+ run
function bind(func, context) {
return function() {
return func.apply(context, arguments);
return function() {
return func.apply(context, arguments);
};
}
var user = {
firstName: "Вася",
sayHi: function() {
alert(this.firstName);
alert( this.firstName );
}
};
*!*
setTimeout( bind(user.sayHi, user), 1000 );
setTimeout(bind(user.sayHi, user), 1000);
*/!*
```
@ -201,7 +203,7 @@ var user = {
*!*
sayHi: function(who) { // здесь у sayHi есть один аргумент
*/!*
alert(this.firstName + ": Привет, " + who);
alert( this.firstName + ": Привет, " + who );
}
};
@ -228,9 +230,9 @@ sayHi("Маша"); // Вася: Привет, Маша
```js
//+ run
function f(a, b) {
alert(this);
alert(a + b);
function f(a, b) {
alert( this );
alert( a + b );
}
*!*
@ -238,7 +240,7 @@ function f(a, b) {
// var g = bind(f, "Context");
var g = f.bind("Context");
*/!*
g(1, 2); // Context, затем 3
g(1, 2); // Context, затем 3
```
Синтаксис встроенного `bind`:
@ -264,14 +266,14 @@ var wrapper = func.bind(context[, arg1, arg2...])
//+ run
var user = {
firstName: "Вася",
sayHi: function() {
alert(this.firstName);
sayHi: function() {
alert( this.firstName );
}
};
*!*
// setTimeout( bind(user.sayHi, user), 1000 );
setTimeout( user.sayHi.bind(user), 1000 ); // аналог через встроенный метод
setTimeout(user.sayHi.bind(user), 1000); // аналог через встроенный метод
*/!*
```
@ -291,7 +293,7 @@ setTimeout( user.sayHi.bind(user), 1000 ); // аналог через встро
Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:
```js
for(var prop in user) {
for (var prop in user) {
if (typeof user[prop] == 'function') {
user[prop] = user[prop].bind(user);
}
@ -327,9 +329,9 @@ function mul(a, b) {
var double = mul.bind(null, 2); // контекст фиксируем null, он не используется
*/!*
alert( double(3) ); // = mul(2, 3) = 6
alert( double(4) ); // = mul(2, 4) = 8
alert( double(5) ); // = mul(2, 5) = 10
alert( double(3) ); // = mul(2, 3) = 6
alert( double(4) ); // = mul(2, 4) = 8
alert( double(5) ); // = mul(2, 5) = 10
```
При вызове `double` будет передавать свои аргументы исходной функции `mul` после тех, которые указаны в `bind`, то есть в данном случае после зафиксированного первого аргумента `2`.
@ -344,9 +346,9 @@ alert( double(5) ); // = mul(2, 5) = 10
var triple = mul.bind(null, 3); // контекст фиксируем null, он не используется
*/!*
alert( triple(3) ); // = mul(3, 3) = 9
alert( triple(4) ); // = mul(3, 4) = 12
alert( triple(5) ); // = mul(3, 5) = 15
alert( triple(3) ); // = mul(3, 3) = 9
alert( triple(4) ); // = mul(3, 4) = 12
alert( triple(5) ); // = mul(3, 5) = 15
```
При помощи `bind` мы можем получить из функции её "частный вариант" как самостоятельную функцию и дальше передать в `setTimeout` или сделать с ней что-то ещё.
@ -377,12 +379,12 @@ function ask(question, answer, ok, fail) {
ask("Выпустить птичку?", "да", fly, die);
*/!*
function fly() {
alert('улетела :)');
function fly() {
alert( 'улетела :)' );
}
function die() {
alert('птичку жалко :(');
function die() {
alert( 'птичку жалко :(' );
}
```
@ -400,7 +402,7 @@ setTimeout(function() {
<li>...Либо использовать `bind`:
```js
setTimeout( obj.func.bind(obj) );
setTimeout(obj.func.bind(obj));
```
</li>
<li>Вызов `bind` часто используют для привязки функции к контексту, чтобы затем присвоить её в обычную переменную и вызывать уже без явного указания объекта.</li>

View file

@ -2,7 +2,7 @@
```js
//+ run
function work(a) {
function work(a) {
/*...*/ // work - произвольная функция, один аргумент
}
@ -10,9 +10,9 @@ function makeLogging(f, log) {
*!*
function wrapper(a) {
log.push(a);
return f.call(this, a);
}
log.push(a);
return f.call(this, a);
}
*/!*
return wrapper;
@ -24,7 +24,7 @@ work = makeLogging(work, log);
work(1); // 1
work(5); // 5
for(var i=0; i<log.length; i++) {
for (var i = 0; i < log.length; i++) {
alert( 'Лог:' + log[i] ); // "Лог:1", затем "Лог:5"
}
```

View file

@ -11,7 +11,7 @@
Работать должно так:
```js
function work(a) {
function work(a) {
/* ... */ // work - произвольная функция, один аргумент
}
@ -23,7 +23,7 @@ work = makeLogging(work, log);
work(1); // 1, добавлено в log
work(5); // 5, добавлено в log
for(var i=0; i<log.length; i++) {
for (var i = 0; i < log.length; i++) {
*!*
alert( 'Лог:' + log[i] ); // "Лог:1", затем "Лог:5"
*/!*

View file

@ -4,17 +4,17 @@
```js
//+ run
function work(a, b) {
alert(a + b); // work - произвольная функция
function work(a, b) {
alert( a + b ); // work - произвольная функция
}
function makeLogging(f, log) {
*!*
function wrapper() {
log.push([].slice.call(arguments));
return f.apply(this, arguments);
}
log.push([].slice.call(arguments));
return f.apply(this, arguments);
}
*/!*
return wrapper;
@ -26,7 +26,7 @@ work = makeLogging(work, log);
work(1, 2); // 3
work(4, 5); // 9
for(var i=0; i<log.length; i++) {
for (var i = 0; i < log.length; i++) {
var args = log[i]; // массив из аргументов i-го вызова
alert( 'Лог:' + args.join() ); // "Лог:1,2", "Лог:4,5"
}

View file

@ -9,8 +9,8 @@
Работать должно так:
```js
function work(a, b) {
alert(a + b); // work - произвольная функция
function work(a, b) {
alert( a + b ); // work - произвольная функция
}
function makeLogging(f, log) { /* ваш код */ }
@ -21,7 +21,7 @@ work = makeLogging(work, log);
work(1, 2); // 3
work(4, 5); // 9
for(var i=0; i<log.length; i++) {
for (var i = 0; i < log.length; i++) {
var args = log[i]; // массив из аргументов i-го вызова
alert( 'Лог:' + args.join() ); // "Лог:1,2", "Лог:4,5"
}

View file

@ -1,7 +1,7 @@
Запоминать результаты вызова функции будем в замыкании, в объекте `cache: { ключ:значение }`.
```js
//+ run
//+ run no-beautify
function f(x) {
return Math.random()*x;
}

View file

@ -14,8 +14,8 @@
Должно работать так:
```js
function f(x) {
return Math.random()*x; // random для удобства тестирования
function f(x) {
return Math.random() * x; // random для удобства тестирования
}
function makeCaching(f) { /* ваш код */ }

View file

@ -16,8 +16,8 @@ JavaScript предоставляет удивительно гибкие воз
```js
function bind(func, context) {
return function() {
return func.apply(context, arguments);
return function() {
return func.apply(context, arguments);
};
}
```
@ -32,7 +32,7 @@ function bind(func, context) {
Использование:
```js
function f(x) { } // любая функция
function f(x) {} // любая функция
var timers = {}; // объект для таймеров
@ -44,7 +44,7 @@ f(1);
f(2);
f(3); // функция работает как раньше, но время подсчитывается
alert(timers.myFunc); // общее время выполнения всех вызовов f
alert( timers.myFunc ); // общее время выполнения всех вызовов f
```
При помощи декоратора `timingDecorator` мы сможем взять произвольную функцию и одним движением руки прикрутить к ней измеритель времени.
@ -71,7 +71,7 @@ function timingDecorator(f, timer) {
// функция может быть произвольной, например такой:
function fibonacci(n) {
return (n > 2) ? fibonacci(n-1) + fibonacci(n-2) : 1;
return (n > 2) ? fibonacci(n - 1) + fibonacci(n - 2) : 1;
}
*!*
@ -86,7 +86,7 @@ alert( fibonacci(20) ); // 6765
*!*
// в любой момент можно получить общее количество времени на вызовы
alert( timers.fibo + 'мс' );
alert( timers.fibo + 'мс' );
*/!*
```
@ -105,6 +105,7 @@ var result = f.apply(this, arguments); // (*)
Например:
```js
//+ no-beautify
function sum(a, b) {
return a + b;
}
@ -134,9 +135,9 @@ function checkNumber(value) {
// второй аргумент checks - массив с функциями для проверки
function typeCheck(f, checks) {
return function() {
for(var i=0; i<arguments.length; i++) {
for (var i = 0; i < arguments.length; i++) {
if (!checks[i](arguments[i])) {
alert("Некорректный тип аргумента номер " + i);
alert( "Некорректный тип аргумента номер " + i );
return;
}
}
@ -154,7 +155,7 @@ sum = typeCheck(sum, [checkNumber, checkNumber]); // оба аргумента -
*/!*
// пользуемся функцией как обычно
alert( sum(1,2 ) ); // 3, все хорошо
alert( sum(1, 2) ); // 3, все хорошо
*!*
// а вот так - будет ошибка
@ -180,10 +181,10 @@ sum(1, ["array", "in", "sum?!?"]); // некорректный аргумент
```js
function checkPermissionDecorator(f) {
return function() {
if ( isAdmin() ) {
return f.apply(this, arguments);
if (isAdmin()) {
return f.apply(this, arguments);
}
alert('Недостаточно прав');
alert( 'Недостаточно прав' );
}
}
```
@ -191,6 +192,7 @@ function checkPermissionDecorator(f) {
Использование декоратора:
```js
//+ no-beautify
function save() { ... }
save = checkPermissionDecorator(save);