まず、基本的なエントリー
1、オブジェクト
JSでは、物事がどのオブジェクト、オブジェクトは通常のオブジェクトと関数オブジェクトに分割して、オブジェクトである、JSのための機能が内蔵された関数オブジェクト。
年OBJ1 = {} obj2が聞かせて = 新しい オブジェクト() OBJ3ましょう = 新しいFUN1() 関数FUN1(){} せfun2 = 関数(){} 聞かせてfun3 = 新機能('いくつかの'、'にconsole.log(一部)' ) // JSは、組み込みの関数オブジェクト はconsole.log(typeof演算 オブジェクト)// 関数 はconsole.log(typeof演算機能)// 機能 // 普通对象 はconsole.log(typeof演算 OBJ1)// オブジェクト はconsole.log(typeof演算 OBJ2)// オブジェクト はconsole.log(typeof演算 OBJ3)// オブジェクト // 函数对象 はconsole.log(typeof演算 FUN1)// 関数 はconsole.log(typeof演算 fun2)// 関数 はconsole.log(typeof演算 fun3)// 機能
新機能()関数オブジェクトによって作成された任意のオブジェクトは、他の人は普通のオブジェクトであり、関数、オブジェクトを新しい機能(を使用して作成された)の
2、コンストラクタ
機能はFoo(名前、年齢){ // この時点Fooの この .nameの= 名前 この .age = 年齢 これ。クラス = 「クラス」 // このデフォルトは、//この行は持って返す } // のFooの例 聞かせてF = 新新を FOO(「ディラン、25)
各インスタンスは、オブジェクト自体を指すコンストラクタ(コンストラクタ)性質を有します
f.constructor ===フー// 真
コンストラクタ自体が通常の機能の間には差がないが、最初の文字を大文字にする一般的な調節するために、関数です。通常の機能は、新しいコンストラクタの例を生成するために使用されるコンストラクタ関数の違いは、直接呼び出しは、通常の関数です。
JS自体は、クラスの実装を提供していません。(ES6でclassキーワードを導入したがシンタックスシュガーには、JavaScriptがまだプロトタイプに基づいています)
3、拡張されたコンストラクタ
- = {}は、実際に=新しいオブジェクト()のシンタックスシュガーを聞かせてみましょう
- = []実際に=新しいアレイ()のシンタックスシュガーを聞かせてみましょう
- 関数はFoo(){...}は実際VARはFoo =新しい機能(...)
- INSTANCEOF変数コンストラクタ場合、関数が使用されてもよい判定する
4、シンボルは、コンストラクタのそれであります
シンボルは、基本データ型である、それは新しいシンボル()構文をサポートしていないため、我々は直接記号()缶を使用して、コンストラクタではありません
名前=シンボル(聞かせて「名前」) 聞かせてNAME1は = 新しい記号(「NAME1」)// シンボルがコンストラクタではありません
しかし、シンボルは()コンストラクタプロパティに取得することができます
名前=シンボル(聞かせて「名前」) console.log(name.constructor)// ƒ記号(){[ネイティブコード]}
このコンストラクタはそれを、実際にシンボルのプロトタイプです
console.log(Symbol.prototype.constructor)// ƒ記号(){[ネイティブコード]}
シンボルのために、あなたはまた、次の点を知っておく必要があります。
シンボル戻り値記号()だけです
console.log(記号("名前")===記号("名前"))// 偽
グローバルにユニークな記号はSymbol.for(キー)により取得することができます
console.log(シンボル。用("名前")===シンボル。のための("名前"))// 真
これは、レジストリのシンボルから対応するシンボルが実行されている見つけ、見つかった場合、それを返し、そうでない場合は、キーに関連付けられた新しいシンボルを作成し、グローバルシンボルレジストリを置きます
Symbol.iterator:オブジェクトの反復子を返します。
// 契約反復を達成する、反復が反復子であってもよい:Symbol.interatorの 関数createIterator(アイテム){ 私は聞かせて 0 = リターンを{ 次:関数(){ 聞かせて行なわ =(I> = items.length) VaRの値=!行なわ?項目[I ++]:未定義の リターン{ 行われ、 値 } }、 [Symbol.iterator]:関数()は、{ 戻り 、これを } } } CONST反復子 = createIterator([1,2,3 ]) console.log(イテレータ)// {次:ƒ、シンボル(Symbol.iterator):ƒ} はconsole.log([...イテレータ])// [1,2,3]
Symbol.toPrimitive:基本データ型へのオブジェクト
// Symbol.toPrimitiveは(ES6のために後に)アンボクシング操作を達成 OBJ =せて{ valueOf:() => {にconsole.log("のvalueOf" )。リターン{}}、 toString() => {にconsole.log("のtoString" )。リターン{}} } OBJ [Symbol.toPrimitive] =()=> { console.log("たToPrimitive" ) の戻り は"hello" } console.log(OBJ + "" ) // たToPrimitive // ハロー
Symbol.toStringTag:文字列説明デフォルト値は、オブジェクトを設定するために使用
// 代わりに[クラス]属性のSymbol.toStringTag(開始ES5) させO = { [Symbol.toStringTag]:"MyObjectに" } console.log(O + "" )// [オブジェクトMyObjectに]
図5は、値は読み取り専用で、コンストラクタのですか?
コンストラクタ属性値の型への参照は、それを変更することができますが、基本的なタイプは読み取り専用です。
参照型
機能親(){ この .VALUE = "親" } 関数子供(){} Child.prototype.constructor =親// コンストラクタのプロトタイプ継承チェーンは再割り当て 子供みましょう = 新しい新しい子()が// の新しいインスタンスを作成します (託児.__ proto__にconsole.logを === Child.prototype)// 真に コンソール。ログ(子供.__プロト__。コンストラクタ===親)// trueに
この場合、子コンストラクタのプロトタイプ親この説明では、オブジェクトコンストラクタ依存属性への参照は安全ではありません
基本タイプ
機能アン(){} てみましょう = 1 an.constructor = アン console.log(an.constructor)// ƒ番号(){[ネイティブコード]}
ネイティブコンストラクタは読み取り専用ですので、これはです。
注:nullとundefinedは、コンストラクタのプロパティではありません
第二に、プロトタイプ
JSでは、各オブジェクトは、独自のプロトタイプを持っています。我々は、オブジェクトのプロパティとメソッドにアクセスすると、JSは、最初のオブジェクト自体のメソッドやプロパティにアクセスします。オブジェクト自体は、これらのプロパティとメソッドが含まれていない場合、オブジェクトに対応するプロトタイプがアクセスされます。
// 构造函数 function Foo(name){ this.name = name } Foo.prototype.alertName = function(){ alert(this.name) } // 创建实例 let f = new Foo('some') f.printName = function(){ console.log(this.name) } // 测试 f.printName() f.alertName()
1、prototype
所有函数都有一个 prototype (显式原型)属性,属性值也是一个普通的对象。对象以其原型为模板,从原型继承方法和属性,这些属性和方法定义在对象的构造器函数的 prototype 属性上,而非对象实例本身。
但有一个例外:Function.prototype.bind() ,它并没有 prototype 属性
Function.prototype.bind() // ƒ () { [native code] }
当我们创建一个函数时
prototype 属性就被自动创建了
从上面这张图可以发现,Foo 对象有一个原型对象,Foo.prototype,其上有两个属性,分别是 constructor 和 __proto__
构造函数 Foo 有一个指向原型的指针,原型 Foo.prototype 有一个指向构造函数的指针 Foo.prototype.constructor,这就是一个循环引用,即
Foo.prototype.constructor === Foo // true
2、__proto__
每个实例对象(object)都有一个隐式原型属性(称之为 __proto__)指向了创建该对象的构造函数的原型。也就是指向了函数的 prototype 属性
function Foo(){} let foo = new Foo() foo.__proto__ === Foo.prototype // true
当 new Foo() 时,__proto__ 被自动创建,即
3、[[Prototype]]
[[Prototype]] 是对象的一个内部属性,外部代码无法直接访问
遵循 ECMAScript标准,someObject.[[Prototype]] 符号用于指向 someObject 的原型
4、注意
__proto__ 属性在 ES6 时才被标准化,以确保 Web 浏览器的兼容性,但是不推荐使用,除了标准化的原因之外还有性能问题。为了更好的支持,推荐使用 Object.getPrototypeOf() 。
通过现代浏览器的操作属性的便利性,可以改变一个对象的 [[Prototype]] 属性,这种行为在每个JavaScript引擎和浏览器中都是一个非常慢且影响性能的操作,使用这种方式来改变和继承属性会对性能影响非常严重,并且性能消耗的时间也不是简单的花费 obj.__proto__ = ... 语句上,它还会影响到所有继承来自该 [[Prototype]] 的对象,如果你关心性能,你就不应该在一个对象中修改它的 [[Prototype]] 。相反,创建一个新的且可以继承 [[Prototype]] 的对象,推荐使用 Object.create()
function An(){} var an = new An() var anran = Object.create(an)
这里 anran 是一个新的空对象,有一个指向对象 an 的指针 __proto__
5、new的实现过程
- 新生成了一个对象
- 链接到原型
- 绑定this
- 返回新对象
function new_object(){ // 创建一个空的对象 let obj = new Object() // 获得构造函数 let Con = [].shift.call(arguments) // 链接到原型(不推荐使用) obj.__proto__ = Con.prototype // 绑定 this,执行构造函数 let result = Con.apply(obj, arguments) // 确保 new 出来的是个对象 return typeof result === 'object' ? result : obj }
优化 new 实现
function create(){ // 1、获得构造函数,同时删除 arguments 中第一个参数 let Con = [].shift.call(arguments) // 2、创建一个空的对象并链接到原型,obj可以访问构造函数原型中的属性 let obj = Object.create(Con.prototype) // 3、绑定 this 实现继承,obj可以访问到构造函数中的属性 let ret = Con.apply(obj, arguments) // 4、优先番红花构造函数返回的对象 return ret instanceof Object ? ret : obj }
6、总结
- 所有的引用类型(数组、对象、函数)都有对象特性,即可自由扩展属性(null除外)
- 所有的引用类型,都有一个 __proto__ 属性,属性值是一个普通的对象,该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null。根据定义,null没有原型,并作为这个原型链中的最后一个环节
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的 __proto__ (即它的构造函数的 prototype) 中寻找
三、原型链
每个对象都拥有一个原型对象,通过 __proto__ 指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向null,这种关系被称为原型链。根据定义,null 没有原型,并作为这个原型链中的最后一环。
原型链的基本思想是利用原型,让一个引用类型继承另一个引用类型的属性及方法
// 构造函数 function Foo(name){ this.name = name } // 创建实例 let f = new Foo('dylan') // 测试 console.log(f.toString()) // [object Object] console.log(f.__proto__) // constructor: ƒ Foo(name) console.log(f.__proto__.__proto__) // constructor: ƒ Object()
f.__proto__ === Foo.prototype ,Foo.prototype 也是一个对象,也有自己的 __proto__ 指向 Object.prototype ,找到 toString()方法
也就是
Function.__proto__.prototype === Object.prototype
下面是原型链继承的例子
function Elem(id){ this.elem = document.getElementById(id) } Elem.prototype.html = function(val){ let elem = this.elem if(val){ elem.innerHTML = val return this // 链式操作 }else{ return elem.innerHTML } } Elem.prototype.on = function(type, fn){ let elem = this.elem elem.addEventListener(type, fn) } let div1 = new Elem('div1') div1.html("<p>hello</p>").on("click", function(){ alert("click") })
四、总结
- Symbol 是基本数据类型,并不是构造函数,因为它不支持语法 new Symbol(),但其原型上用于 constructor 属性,即 Symbol.prototype.constructor
- 引用类型 constructor 是可以修改的,但对于基本类型来说它是只读的,null 和 undefined 没有 constructor 属性
- __proto__ 是每个实例对象都有的属性,prototype 是其构造函数的属性,在实例上并不存在,所以这两个并不一样,但 foo.__proto__ 和 Foo.prototype 指向同一个对象
- __proto__ 属性在 es6 时被标准化,但因为性能问题并不推荐使用,推荐使用 Object.getPrototypeOf()
- 每个对象都拥有一个原型对象,通过 __proto__ 指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层向上,最终指向null,这就是原型链
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的原型中寻找,以及该对象的原型的原型,一层一层向上查找,直到找到一个名字匹配的属性/方法或到达原型链的末尾(null)
1