VII。Functionオブジェクト(以前の知識を確認)
全ての機能オブジェクトのプロト点Function.prototype、それが空の関数(空の関数)であります
Number.__proto__ === Function.prototype // true
Number.constructor == Function //true Boolean.__proto__ === Function.prototype // true Boolean.constructor == Function //true String.__proto__ === Function.prototype // true String.constructor == Function //true // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 Object.__proto__ === Function.prototype // true Object.constructor == Function // true // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 Function.__proto__ === Function.prototype // true Function.constructor == Function //true Array.__proto__ === Function.prototype // true Array.constructor == Function //true RegExp.__proto__ === Function.prototype // true RegExp.constructor == Function //true Error.__proto__ === Function.prototype // true Error.constructor == Function //true Date.__proto__ === Function.prototype // true Date.constructor == Function //true
JavaScriptは(ビルドで)12個のオブジェクト(ES5は、新たにJSONを追加しました)、リストにアクセス可能8つのコンストラクタのコンストラクタ/合計を築いてきました。残りは、直接そのようなグローバル、関数はJSエンジンによって呼び出されたときにのみ作成引数、数学、JSONとしてアクセスすることができない新しいことなくオブジェクトの形で存在します。彼らのプロトでのObject.prototypeです。次のように
Math.__proto__ === Object.prototype // true
Math.construrctor == Object // true JSON.__proto__ === Object.prototype // true JSON.construrctor == Object //true
上記は言った関数オブジェクトのコースカスタムが含まれます。次のように
// 函数声明
function Person() {} // 函数表达式 var Perosn = function() {} console.log(Person.__proto__ === Function.prototype) // true console.log(Man.__proto__ === Function.prototype) // true
これは何を意味するのでしょうか?
**すべてのコンストラクタからのものFunction.prototype
であってもルートビルダーを含む、Object
そしてFunction
自分自身。すべてのコンストラクタは···のFunction.prototypeをプロパティとメソッドを継承します。長さは、コールは、**結合し、適用されます
(あなたは、最初の文を参照してください私たちが言うことを継続し、次のセクションの2番目の文、最初:)穴を掘る必要があります)
Function.prototype
とのみtypeof XXX.prototype
などfunction
のprototype
。他のコンストラクタは、prototype
オブジェクト(既にセクションIIIで説明理由)です。以下のように(とそれを再度確認):
console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype) // object console.log(typeof Number.prototype) // object console.log(typeof Boolean.prototype) // object console.log(typeof String.prototype) // object console.log(typeof Array.prototype) // object console.log(typeof RegExp.prototype) // object console.log(typeof Error.prototype) // object console.log(typeof Date.prototype) // object console.log(typeof Object.prototype) // object
ああ、また上述したように、それは空の関数で、console.log(Function.prototype)
次のを見て(次のセクションでは、あなたにこのことを教えてくれる、注意を払います)
(ビルトインとカスタムを持つ)すべてのコンストラクタを知っている__proto__
すべてをFunction.prototype
、それFunction.prototype
の__proto__
ことはありますか?
私はすべてのJavaScript関数は、第一級市民で聞いて、その後、どのようにそれから反映させることができると信じて?以下の
console.log(Function.prototype.__proto__ === Object.prototype) // true
すべてのコンストラクタのこの説明では、属性を追加/削除するコンストラクタに共通のJSオブジェクトもあります。これに対してtoString、のvalueOf、hasOwnPropertyをと:それはまた、すべてのObject.prototypeのメソッドを継承します。);(また、最初の文、私たちが言うことを継続し、次のセクション、ノー掘削、または単にピットの2番目の文ということを理解すべきです)
最後ののObject.prototype プロトは誰?
Object.prototype.__proto__ === null // true
それはピークに達し、かつnullでいます。(それを理解することができ、戻って第五章を見て、今読みます?)
八。プロトタイプ
ECMAScriptのコアで定義された属性のすべてにおいて、最も興味深いの数はず
prototype
の属性。ECMAScriptのは参照型であるために、prototype
それが本当の方法で彼らはすべてのインスタンスに保存されています。つまり、彼はそのような、言っtoString()
やvaluseOf()
他の方法は、実際に格納されているprototype
名前が、唯一のオブジェクトアクセスの独自のインスタンスで。
- 「JavaScriptの高度なプログラミング」第三版のP116
私たちは、JSのような私たちが使用するいくつかの方法、構築されたことを知っている:
オブジェクトを使用することができconstructor/toString()/valueOf()
、他の方法を、
配列を使用することができmap()/filter()/reducer()
、他の方法を、
で利用可能な数字parseInt()/parseFloat()
他の方法を、
なぜ???
私たちは、関数を作成する場合:
var Person = new Object()
Person
あるObject
インスタンスは、それがPerson
継承するObject
プロトタイプオブジェクトObject.prototype
のすべてのメソッドのを:
上記のプロパティとメソッドを有するオブジェクトの各インスタンス。
だから私はできる
Person.constructor
にも使用すること
Person.hasOwnProperty
。
私たちは、配列を作成する場合:
var num = new Array()
num
あるArray
インスタンスは、それがnum
継承するArray
プロトタイプオブジェクトArray.prototype
のすべてのメソッドのを:
あなたは私をからかっ*** INGをfとしていますか?これはどのようにニマは空の配列ですか???
私たちは、ES5を提供する新しい方法を使用することができます
Object.getOwnPropertyNames
(すべての取得 非可算属性を含む )の属性名が 含まれていない
prototy
プロパティには、
配列を返します。
var arrayAllKeys = Array.prototype; // [] 空数组
// 只得到 arrayAllKeys 这个对象里所有的属性名(不会去找 arrayAllKeys.prototype 中的属性)
console.log(Object.getOwnPropertyNames(arrayAllKeys)); /* 输出: ["length", "constructor", "toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight", "entries", "keys", "copyWithin", "find", "findIndex", "fill"] */
だから、あなたはそれが行くにはそれほど方法することができ、なぜ配列を、宣言を理解するだろう。
慎重にあなたが見つけなければならないObject.getOwnPropertyNames(arrayAllKeys)
、アレイの出力がないconstructor/hasOwnPrototype
などのオブジェクトのメソッド(あなたは確かに見つけることができませんでした)。
しかし、また、定義されたカジュアルの配列は、これらのメソッドを使用します
var num = [1];
console.log(num.hasOwnPrototype()) // false (输出布尔值而不是报错)
なぜ ???
のでArray.prototype
、これらの方法ではありませんでしたが、それは(プロトタイプオブジェクトを持っています__proto__
):
// 上面我们说了 Object.prototype 就是一个普通对象。
Array.prototype.__proto__ == Object.prototype
だから、Array.prototype
あなたが使用する場合、すべてのオブジェクトのメソッドを継承しnum.hasOwnPrototype()
た時間を、JSは、まずそのコンストラクタ(チェックするArray
)プロトタイプオブジェクトをArray.prototype
そこに持ってhasOwnPrototype()
、見られない方法を、その後、チェックしていきArray.prototype
プロトタイプオブジェクトArray.prototype.__proto__
はそのような方法はありませんが。
私たちは、関数を作成する場合:
var f = new Function("x","return x*x;"); //当然你也可以这么创建 f = function(x){ return x*x } console.log(f.arguments) // arguments 方法从哪里来的? console.log(f.call(window)) // call 方法从哪里来的? console.log(Function.prototype) // function() {} (一个空的函数) console.log(Object.getOwnPropertyNames(Function.prototype)); /* 输出 ["length", "name", "arguments", "caller", "constructor", "bind", "toString", "call", "apply"] */
私たちはこの文の第八節を確認してみましょう:
所有函数对象proto都指向
Function.prototype
,它是一个空函数(Empty function)
嗯,我们验证了它就是空函数。不过不要忽略前半句。我们枚举出了它的所有的方法,所以所有的函数对象都能用,比如:
如果你还没搞懂啥是函数对象?
还有,我建议你可以再复习下为什么:
Function.prototype
是唯一一个typeof XXX.prototype为 “function”的prototype
我猜你肯定忘了。
九. 复习一下
第八小节我们总结了:
所有函数对象的 __proto__ 都指向 Function.prototype,它是一个空函数(Empty function)
但是你可别忘了在第三小节我们总结的:
所有对象的 __proto__ 都指向其构造器的 prototype
咦,我找了半天怎么没找到这句话……
我们下面再复习下这句话。
先看看 JS 内置构造器:
var obj = {name: 'jack'}
var arr = [1,2,3] var reg = /hello/g var date = new Date var err = new Error('exception') console.log(obj.__proto__ === Object.prototype) // true console.log(arr.__proto__ === Array.prototype) // true console.log(reg.__proto__ === RegExp.prototype) // true console.log(date.__proto__ === Date.prototype) // true console.log(err.__proto__ === Error.prototype) // true
再看看自定义的构造器,这里定义了一个 Person
:
function Person(name) {
this.name = name; } var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true
p
是 Person
的实例对象,p
的内部原型总是指向其构造器 Person
的原型对象 prototype
。
每个对象都有一个 constructor
属性,可以获取它的构造器,因此以下打印结果也是恒等的:
function Person(name) {
this.name = name } var p = new Person('jack') console.log(p.__proto__ === p.constructor.prototype) // true
上面的Person
没有给其原型添加属性或方法,这里给其原型添加一个getName
方法:
function Person(name) {
this.name = name } // 修改原型 Person.prototype.getName = function() {} var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // true
可以看到p.__proto__
与Person.prototype
,p.constructor.prototype
都是恒等的,即都指向同一个对象。
如果换一种方式设置原型,结果就有些不同了:
function Person(name) {
this.name = name } // 重写原型 Person.prototype = { getName: function() {} } var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // false
这里直接重写了 Person.prototype
(注意:上一个示例是修改原型)。输出结果可以看出p.__proto__
仍然指向的是Person.prototype
,而不是p.constructor.prototype
。
这也很好理解,给Person.prototype
赋值的是一个对象直接量{getName: function(){}}
,使用对象直接量方式定义的对象其构造器(constructor
)指向的是根构造器Object
,Object.prototype
是一个空对象{}
,{}
自然与{getName: function(){}}
不等。如下:
var p = {}
console.log(Object.prototype) // 为一个空的对象{}
console.log(p.constructor === Object) // 对象直接量方式定义的对象其constructor为Object console.log(p.constructor.prototype === Object.prototype) // 为true,不解释(๑ˇ3ˇ๑)
十. 原型链(再复习一下:)
下面这个例子你应该能明白了!
function Person(){}
var person1 = new Person(); console.log(person1.__proto__ === Person.prototype); // true console.log(Person.prototype.__proto__ === Object.prototype) //true console.log(Object.prototype.__proto__) //null Person.__proto__ == Function.prototype; //true console.log(Function.prototype)// function(){} (空函数) var num = new Array() console.log(num.__proto__ == Array.prototype) // true console.log( Array.prototype.__proto__ == Object.prototype) // true console.log(Array.prototype) // [] (空数组) console.log(Object.prototype.__proto__) //null console.log(Array.__proto__ == Function.prototype)// true
疑点解惑:
-
Object.__proto__ === Function.prototype // true
Object
是函数对象,是通过new Function()
创建的,所以Object.__proto__
指向Function.prototype
。(参照第八小节:「所有函数对象的__proto__
都指向Function.prototype
」) -
Function.__proto__ === Function.prototype // true
Function
也是对象函数,也是通过new Function()
创建,所以Function.__proto__
指向Function.prototype
。
自己是由自己创建的,好像不符合逻辑,但仔细想想,现实世界也有些类似,你是怎么来的,你妈生的,你妈怎么来的,你姥姥生的,……类人猿进化来的,那类人猿从哪来,一直追溯下去……,就是无,(NULL生万物)
正如《道德经》里所说“无,名天地之始”。
Function.prototype.__proto__ === Object.prototype //true
其实这一点我也有点困惑,不过也可以试着解释一下。
Function.prototype
是个函数对象,理论上他的__proto__
应该指向Function.prototype
,就是他自己,自己指向自己,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype
。Object.prototype.__proto__ === null
,保证原型链能够正常结束。
十一 总结
- 原型和原型链是JS实现继承的一种模型。
- 原型链的形成是真正是靠
__proto__
而非prototype
要深入理解这句话,我们再举个例子,看看前面你真的理解了吗?
var animal = function(){};
var dog = function(){}; animal.price = 2000; dog.prototype = animal; var tidy = new dog(); console.log(dog.price) //undefined console.log(tidy.price) // 2000
这里解释一下:
var dog = function(){};
dog.prototype.price = 2000; var tidy = new dog(); console.log(tidy.price); // 2000 console.log(dog.price); //undefined
var dog = function(){};
var tidy = new dog(); tidy.price = 2000; console.log(dog.price); //undefined
这个明白吧?想一想我们上面说过这句话:
实例(
tidy
)和 原型对象(dog.prototype
)存在一个连接。不过,要明确的真正重要的一点就是,这个连接存在于实例(tidy
)与构造函数的原型对象(dog.prototype
)之间,而不是存在于实例(tidy
)与构造函数(dog
)之间。
聪明的你肯定想通了吧