JavaScript 原型链/Function/Object/属性搜索原则/instanceof


1. 原型链
(1)对象的原型链
绘制原型链的结构(三角形)
[] -> Array.prototype -> Object.prototype -> null
p -> Person.prototype -> Object.prototype -> null
{} -> Object.prototype -> null

原型式继承
①  原型式继承就是原型中提供的成员,我们的实例对象可以直接使用,好像是自己的一样
② 所谓的 操作原型式继承就是在对原型链做增加、删除、修改的操作
//如果有 animal
function Person() {}
Person.prototype = animal;
var p = new Person();
//原型链(相当于在原来的链中增加了一节):
p -> Person.prototype(其实是animal) -> Animal.prototype -> Object.prototype

(2)函数的原型链
绘制原型链的结构

(3)完整原型链
结合对象的原型链和函数的原型链,将其绘制到一起

2. Function

Function 是函数的构造函数
(函数的constructor属性就等于Function)
借助这个理论可以绘制函数的对象原型链(三角形)


3. 利用 Function 构建函数

定义一个函数需要有:
① 参数
② 函数体
③ 返回值
 这三个东西本质上是代码,js允许将这些代码以字符串的形式放到 Function 中,来构建函数。
语法:
var func = new Function( 参数1, 参数2, ..., 函数体 )

例:设计一个函数,求两个数字的和
var sum = new Function( 'a', 'b', 'return a+b;' );

函数体太多内容时,一般来说使用字符串拼接 + 折行显示。
为了解决麻烦,引入ES6中的可换行字符串,数字键左边的左撇号。
第三种方法,在页面添加一个标签
<script type="text/template" id="code">
    函数体
</script>






document.createElementFragment
应用:所见即所得的编辑器

4. 绘制函数的原型链结构

函数的构造函数是 Function
Function 是什么数据类型?函数
结论: Function 是 Function 的构造函数
① Function.prototype 是原型对象,Function 就继承自该原型对象
② Function 是 Function 的实例
Function instanceof Function 为 true
推论:
Function instanceof Object 为 true
Object instanceof Function 为 true


Function 也是函数,因此 Function 是 Function 的实例
这是原型链中唯一的特例(两个对象构成三角形关系)
因此,Function 的 .prototype 与 .__proto__ 指向同一个对象


一般的函数不是这样
function Person() {}
1) Person.prototype 是用于描述实例对象的, 表示 new Person() 所继承的对象. 
2) Person.__proto__ 是用于描述 Person 这个函数作为对象来说是 Function 的实例. 是 Function.prototype. 
因此一般的函数, 这两个属性不是同一个对象.

Function -> Function.prototype -> Object.prototype -> null

例如:希望让js中的所有函数都有 inherit 方法,来实现派生
Function.prototype.inherit = function() {}
Object.prototype.inherit = function() {}

5. __proto__

作用:让实例对象可以访问到该原型对象
早期是无法直接访问的,如果早期需要访问,用 obj.constructor.prototype
这样不方便调试,很麻烦,因此在火狐中率先引入 __proto__ 属性,用于让对象直接访问其原型。由于当时是非标准属性,因此使用__作为前后缀。但是由于非常好用,今天的浏览器基本都实现了该属性,但依旧没有标准化(IE8不支持)。因此在开发时,不建议用,也不可以使用该属性。
调试原型链结构:obj.__protp__.__proto__.__proto__.__proto__
因此在实际开发中,使用 __proto__ 只是用于调试查看,即只用其读的特性。

以前的结论:
① 只有函数有 prototype 属性
② 只有实例对象有 __proto__ 属性

今天的结论:函数也是对象,函数也有 __proto__ 属性
① 函数有 prototype 属性,该属性指向原型,该原型被函数的实例对象所继承
② 函数有 __proto__ 属性,该属性指向原型,该函数继承自该原型
③ 函数的 __proto__ 就是 Function.prototype,Function.__proto__ 就是 Function.prototype



6. Object 本身就是一个构造函数

function Person() { }
// Object 与 Person 是等同地位的
就像人是由基因决定的,无论是父亲还是儿子都有基因
基因就好比构造函数,决定对象有什么成员,也就是对象长什么样子

结论:
① 对象应该都有原型
② 实例对象继承自 构造函数.prototype
③ 原型对象也有继承的关系
④ 只有原型中有 constructor,constructor 决定了原型的名字
如果 constructor 是 AAA, 那么 这个原型就是 AAA.prototype

到此我们得到对象的原型链结构
1) [ ]
    [ ] -> Array.prototype -> Object.prototype -> null
2) p: Person
    p -> Person.prototype -> Object.prototype -> null
3) { }
    { } -> Object.prototype -> null

结合原型式继承. 什么是原型式继承? 怎么操作原型式继承?
1) 原型式继承就是 原型中提供的成员, 我们的实例对象可以直接使用, 好像是自己的一样.
2) 所谓的操作原型式继承就是在 对原型链做增加, 删除, 修改的操作.
//如果有 animal
function Person () {}
Person.prototype = animal;
var p = new Person();
...
//原型链:
//p -> Person.prototype( 其实是 animal ) -> Animal.prototype -> Object.prototype -> null

7. 字面量的原型链

所谓的字面量就是在代码中的写出来以后有数据、有大小、有类型等具体的数据。
常见的字面量有:
数字    1,2,123, ...
布尔值    true,false
字符串    'xxxxxx'
对象    { },注意有时需要使用( { } )
数组    [ ]
函数    function() { }
正则表达式    /.+/
字面量中,凡是引用类型的都有对应的构造函数,因此原型链的绘制就简单了。



8. 属性搜索原则
错误的结论:如果一个对象中没有对应的方法,但是其原型中提供了该方法,该对象可以直接使用原型中的方法,就好像是自己的方法一样。

所谓的属性搜索原则是说,对象在访问某一个成员的时候,采用的访问规则
① 首先在当前对象中查找是否有对应的成员(在自己的代码结构中找)
② 如果有,则使用该成员,并停止查找,如果没有,则到其原型对象中查找对应的成员
③ 如果其原型中有该成员,则使用,并停止查找,否则继续往原型的原型中查找
④ 如此往复
⑤ 直到 Object.prototype 中,如果还没有对应的成员,则返回 undefined

访问依照 就近原则


9. 原型成员的读写

在读取数据的时候,如果当前对象没有该数据,原型中提供了该数据,那么可以直接访问该数据。但是,如果是修改属性,不会对原型中的数据造成任何影响,只会在当前对象中新创建一个该成员,再次读取该成员时,就不再走原型上的数据了。


10. instanceof 运算符

语法: object(要检测的对象) instanceof constructor(某个构造函数)  -> boolean
含义:检测 constructor.prototype 是否存在于 object 的原型链上,即一个对象在其原型链中是否存在一个构造函数的 prototype 属性

案例1:
function Person() {}
var p = new Person();
p instanceof Person   // true
//p 的原型链:
//p -> Person.prototype -> Object.prototype -> null

案例2:
function Person() {}
var p1 = new Person();
Person.prototype = {};
var p2 = new Person();
console.log( p1 instanceof Person );//false
console.log( p2 instanceof Person );//true
//分析原型链:
//p1 -> Person.prototype(原始的类型) -> Object.prototype -> null
//p2 -> Person.prototype( { } ) -> Object.prototype -> null
//现在 Person.prototype 是 { }
//p1 instanceof Person 表示检测 { } 是否在 p1 的原型链中 -> false
//p2 instanceof Person 表示检测 { } 是否在 p2 的原型链中 -> true



案例3:
console.log(Function instanceof Object);//true
//Object.prototype 是否在 Function 的原型链中

console.log(Function instanceof Function);//true
//Function.prototype 是否在 Function 的原型链中

console.log(Object instanceof Object);//true
//Object.prototype 是否在 Object 的原型链中

console.log(Object instanceof Function);//true
//Function.prototype 是否在 Object 的原型链中
//Object -> Function.prototype -> Object.prototype -> null

    Object、Function、Array 等等都是构造函数,都是函数 
=> 而所有函数都是构造函数 Function 的实例 
=> 从原型链机制来说,所有函数都能通过原型链找到创建他们的构造函数 Function 的构造原型 Function.prototype 对象
=> Object instanceof Function 的结果为 true

    又因为 Function.prototype 是一个对象
=> Function.prototype 的构造函数是 Object
=> 从原型链机制来说,所有对象都能通过原型链找到创建它们的构造函数 Object 的构造原型 Object.prototype 对象
=> Function instanceof Object 的结果为 true

    Function 是构造函数
=> Function 是函数对象
=> 函数对象都是由 Function 构造函数创建而来的,从原型链机制来说,函数对象的原型链中存在 Function.prototype 
=> Function.prototype 在构造函数 Function 的原型链中被查到
=> Function instanceof Function 的结果为 true

结论:在 js 中,一切皆对象,它们全部继承自 Object,或者说所有对象的原型链的根节点都是 Object.prototype
js原型链-jk.png

11. Object.prototype 的常用成员

(1)hasOwnProperty

语法: 对象.hasOwnProperty( '属性名称' ) -> boolean
含义:判断某个属性是否是当前对象提供,而不是原型提供的
作用:一般在写混入的时候会使用
for(var k in obj){
    //只混入当前对象的属性
    if(obj.hasOwnProperty(k)){
        dist[k] = obj[k];
    }
}

(2)isPrototypeOf

语法: 对象A.isPrototypeOf( 对象B ) -> boolean
含义:判断 对象A 是不是 对象B 的原型(实质上是判断对象A是否在对象B的原型链中)

(3)prototypeIsEnumerable

语法: 对象A.prototypeIsEnumerable(  '属性名称'  ) -> boolean
含义:判断当前对象是否提供对应的属性,同时该属性可枚举
在 js 中一般添加的成员都是可枚举的(所谓的可枚举就是利用 for in 可以遍历出来)
在 ES5 之前,所有的自定义属性都是可枚举的,是无法控制的。因此该方法很少使用。
在 ES5 之后,引入了 Object.definedPrototype 等方法,允许用户定义不可枚举的属性。

(4)valueOf

将对象转换成基本类型(实现的不好,平时不会用,都是自己实现该方法去转换)
内部的实现就是 return this,把自己返回了,并没有实现功能,没什么用处

(5)toString

将对象转换成字符串(实现的不好,平时不会用,都是自己实现该方法去转换)
console.log(s.toString());//[object Object](假设s是个对象)
console.log([].toString());//默认将数组元素用逗号连接,因此会打印空
//数组自己有 toString 方法

(6)toLocaleString

将对象转换成本地字符串(实现的不好,平时不会用,都是自己实现该方法去转换)
例:


计算机毫秒数从1970年开始算






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值