Skip to content

Instantly share code, notes, and snippets.

@kajirikajiri
Last active June 14, 2020 11:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kajirikajiri/79830a6ba4927cd6861d7b4a3896e984 to your computer and use it in GitHub Desktop.
Save kajirikajiri/79830a6ba4927cd6861d7b4a3896e984 to your computer and use it in GitHub Desktop.
BOOK: You Dont Know JS Up and Going

Kyle Simpson JavaScript_Up_and_Going-EN You_Dont_Know_JS_Up_and_Going

Non-JavaScript

So far, the only things we’ve covered are in the JS language itself. The reality is that most JS is written to run in and interact with environments like browsers. A good chunk of the stuff that you write in your code is, strictly speaking, not directly controlled by JavaScript. That probably sounds a little strange. The most common non-JavaScript JavaScript you’ll encounter is the DOM API. For example:

var el = document.getElementByID( "foo" );

The document variable exists as a global variable when your code is running in a browser. It’s not provided by the JS engine, nor is it particularly controlled by the JavaScript specification. It takes the form of something that looks an awful lot like a normal JS object, but it’s not really exactly that. It’s a special object, often called a “host object.”

ここまでは、JS言語そのものについてしか取り上げてきませんでした。現実には、ほとんどの JS はブラウザのような環境で動作し、相互作用するように書かれています。あなたのコードに書かれているもののかなりの部分は、厳密に言えば、JavaScriptによって直接制御されるものではありません。これは少し奇妙に聞こえるかもしれません。あなたが遭遇する最も一般的なJavaScript以外のものは、DOM APIです。例えば、以下のようになります。

var el = document.getElementByID( "foo" ).

ドキュメント変数は、コードがブラウザで実行されているときにグローバル変数として存在します。これはJSエンジンによって提供されるものではありませんし、JavaScriptの仕様によって特に制御されるものでもありません。通常のJSオブジェクトのような形をしていますが、実際にはそうではありません。これは特別なオブジェクトで、しばしば "ホストオブジェクト" と呼ばれます。

ホストオブジェクトっていうのか。 document, alert, console.logとかがそれなのね。


Scope & Closures Perhaps one of the most fundamental things you’ll need to quickly come to terms with is how scoping of variables really works in JavaScript. It’s not enough to have anecdotal fuzzy beliefs about scope.

The Scope & Closures title starts by debunking the common misconception that JS is an “interpreted language” and therefore not compiled. Nope. The JS engine compiles your code right before (and sometimes during!) execution. So we use some deeper understanding of the compiler’s approach to our code to understand how it finds and deals with variable and function declarations. Along the way, we see the typical metaphor for JS variable scope management, “hoisting.” This critical understanding of “lexical scope” is what we then base our exploration of closure on for the last chapter of the book. Closure is perhaps the single most important concept in all of JS, but if you haven’t first grasped firmly how scope works, closure will likely remain beyond your grasp. One important application of closure is the module pattern, as we briefly introduced in this book in Chapter 2. The module pattern is perhaps the most prevalent code organization pattern in all of JavaScript; deep understanding of it should be one of your highest priorities.

スコープ&クロージャー おそらく、すぐに理解しなければならない最も基本的なことの一つは、 JavaScript で変数のスコープが実際にどのように機能するかということでしょう。スコープについての経験則的な曖昧な思い込みだけでは十分ではありません。

Scope & Closures のタイトルは、JS は「解釈される言語」だからコンパイルされないというよくある誤解を覆すことから始まります。そうではありません。 JSエンジンは、コードを実行する直前に(時には実行中に!)コンパイルします。そこで、コードに対するコンパイラのアプローチをより深く理解して、変数や関数の宣言をどのように見つけて処理するかを理解します。その過程で、JS の変数スコープ管理の典型的なメタファーである "吊り上げ" が見えてきます。この「語彙的スコープ」の重要な理解は、この本の最終章でクロージャーの探求のベースとなるものです。クロージャーはおそらくJSの中で最も重要な概念ですが、スコープがどのように機能するのかをしっかりと理解していなければ、クロージャーは理解できないままになってしまうでしょう。クロージャの重要な応用例として、第2章で簡単に紹介したモジュールパターンがあります。モジュールパターンは、おそらく JavaScript の中で最も一般的なコード編成パターンです。

okこの辺は理解できてるはず


this Binding Exception Safer this Perhaps a somewhat “safer” practice is to pass a specifically set up object for this that is guaranteed not to be an object that can create problematic side effects in your program. Borrowing terminology from networking (and the military), we can create a “DMZ” (demilitarized zone) object—nothing more special than a completely empty, nondelegated object (see Chapters 5 and 6). If we always pass a DMZ object for ignored this bindings we don’t think we need to care about, we’re sure any hidden/unexpected usage of this will be restricted to the empty object, which insulates our program’s global object from side effects. Since this object is totally empty, I personally like to give it the variable name ø (the lowercase mathematical symbol for the empty set). On many keyboards (like US-layout on Mac), this symbol is easily typed with ⌥+o (Option-o). Some systems also let you set up hotkeys for specific symbols. If you don’t like the ø symbol, or your keyboard doesn’t make it easy to type, you can of course call it whatever you want. Whatever you call the variable, the easiest way to set it up as totally empty is Object.create(null) (see Chapter 5). Object.create(null) is similar to { }, but without the delegation to Object.prototype, so it’s “more empty” than just { }:

おそらく、プログラムに問題のある副作用を引き起こす可能性のあるオブジェクトではないことが保証されている、このために特別に設定されたオブジェクトを渡すのが、多少「安全」な方法でしょう。ネットワーク(と軍)の用語を借りて、"DMZ" (非武装地帯) オブジェクトを作ることができます。もし、気にする必要がないと思われるこのバインディングを無視するために常にDMZオブジェクトを渡すならば、これの隠された/予期せぬ使用法は空のオブジェクトに制限され、プログラムのグローバルオブジェクトを副作用から隔離することになるでしょう。このオブジェクトは完全に空なので、個人的には変数名に ø (空の集合を表す小文字の数学記号) をつけたいと思っています。多くのキーボード(MacのUS-layoutなど)では、この記号は⌥+o(Option-o)と簡単に入力できます。システムによっては、特定の記号にホットキーを設定できるものもあります。もし ø の記号が気に入らない場合や、キーボードで入力しづらい場合は、もちろん好きなように呼び出すことができます。どのように呼んでも、変数を完全に空にする最も簡単な方法は、Object.create(null)です(第5章参照)。Object.cre ate(null) は { } と似ていますが、Object.pro totype へのデリゲーションがないので、単なる { } よりも「より空」です。

https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9 A delegate prototype is an object that serves as a base for another object. When you inherit from a delegate prototype, the new object gets a reference to the prototype.

デリゲート・プロトタイプは、別のオブジェクトのベースとなるオブジェクトです。デリゲートプロトタイプを継承すると、新しいオブジェクトはプロトタイプへの参照を取得します。

継承されたからdelegate なのか?それとも継承できるからdelegate? 両方である気がする。

delegateは代議人とか代理人とか代表か。それを代表、親、として、継承するってかんじか。しらべるか。

https://davidwalsh.name/object-create-null

obj = Object.create({})
{}

obj.prototype
undefined

obj.__proto__
{}

obj.hasOwnProperty
ƒ hasOwnProperty() { [native code] }

dict = Object.create(null)
{}

dict.__proto__
undefined

__proto__とprototype一緒だと思ってた。mdnに__proto__は非推奨って書いてたような??

https://medium.com/javascript-in-plain-english/proto-vs-prototype-in-js-140b9b9c8cd5

In reality, the only true difference between prototype and proto is that the former is a property of a class constructor, while the latter is a property of a class instance. In other words, while iPhone.prototype provides a blueprint for building an iPhone, newPhone.proto affirms that the iPhone has indeed been built according to that specific blueprint. But with regards to the properties and methods present in those two objects… well, they’re exactly the same.

実際には、唯一の 本当 プロトタイプと__proto__の違いは、前者はクラスコンストラクターのプロパティであり、後者はクラスインスタンスのプロパティであることです。つまり、iPhone.prototypeはiPhoneを構築するための青写真を提供しますが、newPhone .__ proto__は、iPhoneが実際にその特定の青写真に従って構築されたことを確認します。しかし、これら2つのオブジェクトに存在するプロパティとメソッドに関しては…まあ、それらはまったく同じです。

prototypeはclass constructorのpropertyで__proto__はclass instanceのpropertyか

https://hackernoon.com/understand-nodejs-javascript-object-inheritance-proto-prototype-class-9bd951700b29

var proto = {
    describe: function () {
        return 'name: '+this.name;
    }
};
var obj = {
    [[Prototype]]: proto,
    name: 'obj'
};

obj.describe

obj.describe()

https://stackoverflow.com/questions/39349478/js-prototype-error-prototype-is-not-defined [[Prototype]] is only specification-speech for an internal property (the link in the prototype chain). To link obj to proto through the prototype chain, you can use Object.create:

表示上のやつか。 こうかな?

var proto = {
    describe: function () {
        return 'name: '+this.name;
    }
};
var obj = Object.create(proto)
obj.name = 'bob'

obj.describe
[Function: describe]

obj.describe()
'name: bob'
function Foo(name) {
  this.name = name;
}

var b = new Foo('b');
var a = new Foo('a');
b.say = function() {
  console.log('Hi from ' + this.whoAmI());
}

console.log(a.__proto__ === Foo.prototype); // true
console.log(a.__proto__ === b.__proto__); // true
console.log(b.__proto__ === Foo.prototype); // true
function Foo(y){
}
undefined

Foo.__proto__
[Function (anonymous)]

Foo.__proto__.__proto__
{}

Foo.__proto__.__proto__.__proto__
null

function Foo(y){
  this.y = y
}
undefined

Foo.prototype
Foo {}

Foo.prototype.prototype
undefined

Foo.prototype.__proto__
{}

Foo.prototype.__proto__
{}

Foo.prototype.__proto__.hoge = 'hoge-'
'hoge-'

{}.hoge
'hoge-'

Foo.prototype.__proto__
{ hoge: 'hoge-' }

prototypeはclass constructorのpropertyで__proto__はclass instanceのproperty

Function.prototypeはFunctionのconstructor

Object <- Object.prototype built-in <- Function.prototype built-in <- いろんな関数 Object <- Object.prototype <- いろんなobject

new Foo する時にはFoo.prototypeが間にいて、そいつからインスタンスが作成されている。

prototype.prototypeはundefined proto.__proto__はあるが最後にnullがいる。たどっていくと、{}がいて、それの中身がnull。 だが、{}の中身を定義することもできる。たぶんなんでもできる。

あ、これか。

Object.create(null) is similar to { }, but without the delegation to Object.prototype, so it’s “more empty” than just { }

そういうことね。Object.create(null)なら、なにも見つけることができない。 けど、{}の場合、{}.__proto__でObject.prototypeが見つかる。だから、more empty。

{}.__proto__
{}

{}.__proto__.__proto__
null
Function.prototype
[Function (anonymous)]

Function.prototype.__proto__
{}

Function.prototype.__proto__.__proto__
null

うんうん。

http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

これも勉強になった。グラフ付きでわかりやすい。


Indirection Another thing to be aware of is that you can (intentionally or not!) create “indirect references” to functions, and in those cases, when that function reference is invoked, the default binding rule also applies. One of the most common ways that indirect references occur is from an assignment:

function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2

The result value of the assignment expression p.foo = o.foo is a reference to just the underlying function object. As such, the effective call-site is just foo(), not p.foo() or o.foo() as you might expect. Per the rules mentioned earlier, the default binding rule applies. Reminder: regardless of how you get to a function invocation using the default binding rule, the strict mode status of the contents of the invoked function making the this reference—not the function call-site—determines the default binding value: either the global object if in non-strict mode or undefined if in strict mode.

もう一つ気をつけなければならないのは、関数への「間接参照」を(意図的にかどうかは別として!)作成することができ、その場合、その関数参照が呼び出されると、デフォルトのバインディングルールも適用されるということです。間接参照が発生する最も一般的な方法の一つは、代入からのものです。

function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2

代入式 p.foo = o.foo の結果値は、基礎となる関数オブジェクトへの参照です。このように、有効なコールサイトは foo() であって p.foo() や o.foo() ではありません。先ほどのルールにしたがって、デフォルトのバインディングルールが適用されます。注意: デフォルトのバインディングルールを使用して関数の呼び出しにたどり着く方法に関わらず、 この参照を行う呼び出された関数の内容のストリクトモードの状態 (関数のコールサイトではなく) がデフォルトのバインディングの値を決定します: 非ストリクトモードの場合はグローバルオブジェクト、 ストリクトモードの場合は未定義のどちらかです。

たしかにそうなるけど、その発想がなかった。なに(p.foo = o.foo)()これ p.fooにo.fooを代入。返り値がfoo()だからそれを()()で呼び出す。と、その時のthisはvar a = 2だから、2が出力される。けど、きもい。


Softening Binding

We saw earlier that hard binding was one strategy for preventing a function call falling back to the default binding rule inadvertently, by forcing it to be bound to a specific this (unless you use new to override it!). The problem is, hard binding greatly reduces the flexibility of a function, preventing manual this override with either implicit binding or even subsequent explicit binding attempts. It would be nice if there was a way to provide a different default for default binding (not global or undefined), while still leaving the function able to be manually this-bound via implicit binding or explicit binding techniques. We can construct a so-called soft binding utility that emulates our desired behavior:

if (!Function.prototype.softBind) {
  Function.prototype.softBind = function(obj) {
  var fn = this;
  // capture any curried parameters
  var curried = [].slice.call( arguments, 1 );
  var bound = function() {
    return fn.apply(
    (!this || this === (window || global)) ?
    obj : this
    curried.concat.apply( curried, arguments )
    );
  };
  bound.prototype = Object.create( fn.prototype );
  return bound;
  };
}

The softBind(..) utility provided here works similarly to the builtin ES5 bind(..) utility, except with our soft binding behavior. It wraps the specified function in logic that checks the this at call-time and if it’s global or undefined, uses a prespecified alternate default (obj). Otherwise the this is left untouched. It also provides optional currying (see the bind(..) discussion earlier). Let’s demonstrate its usage:

function foo() {
  console.log("name: " + this.name);
}
var obj = { name: "obj" },
obj2 = { name: "obj2" },
obj3 = { name: "obj3" };
var fooOBJ = foo.softBind( obj );
fooOBJ(); // name: obj
obj2.foo = foo.softBind(obj);
obj2.foo(); // name: obj2 <---- look!!!
fooOBJ.call( obj3 ); // name: obj3 <---- look!
setTimeout( obj2.foo, 10 );
// name: obj <---- falls back to soft-binding

The soft-bound version of the foo() function can be manually this-bound to obj2 or obj3 as shown, but it falls back to obj if the default binding would otherwise apply.

ハードバインディングは、関数呼び出しが不注意でデフォルトのバインディングルールに戻ってしまうことを防ぐための一つの戦略であることを先に見てきました。問題は、ハードバインディングは関数の柔軟性を大幅に低下させ、暗黙のバインディングやそれに続く明示的なバインディングの試みによる手動のオーバーライドを防ぎます。デフォルトバインディングに異なるデフォルトを提供する方法があればいいのですが(グローバルや未定義ではなく)、暗黙のバインディングや明示的なバインディング技術を使って手動でこのバインディングを行うことができるようにしておきます。希望する動作をエミュレートする、いわゆるソフトバインディングユーティリティを構築することができます。

if (!Function.prototype.softBind) {
  Function.prototype.softBind = function(obj) {
  var fn = this;
  // capture any curried parameters
  var curried = [].slice.call( arguments, 1 );
  var bound = function() {
    return fn.apply(
    (!this || this === (window || global)) ?
    obj : this
    curried.concat.apply( curried, arguments )
    );
  };
  bound.prototype = Object.create( fn.prototype );
  return bound;
  };
}

ここで提供される softBind(...) ユーティリティは、ソフトバインディングの動作を除いて、組み込みの ES5 bind(...) ユーティリティと同様に動作します。このユーティリティは、指定された関数をロジックでラップし、コール時に this をチェックし、グローバルか未定義の場合は事前に指定された代替のデフォルト (obj) を使用します。それ以外の場合は this はそのままになります。また、オプションのカーリングも提供しています (先ほどの bind(....) の説明を参照してください)。では、その使い方を実演してみましょう。

function foo() {
  console.log("name: " + this.name);
}
var obj = { name: "obj" },
obj2 = { name: "obj2" },
obj3 = { name: "obj3" };
var fooOBJ = foo.softBind( obj );
fooOBJ(); // name: obj
obj2.foo = foo.softBind(obj);
obj2.foo(); // name: obj2 <---- look!!!
fooOBJ.call( obj3 ); // name: obj3 <---- look!
setTimeout( obj2.foo, 10 );
// name: obj <---- falls back to soft-binding

ソフトバウンド版の foo() 関数は、図のように手動で obj2 あるいは obj3 にバインドすることができますが、デフォルトのバインディングが適用される場合は obj にフォールバックします。

.bind()はいいや。わかる

next p.35

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment