由数组的面向对象原型体系来解析Function和Object两大内置类的复杂关系

首先来大概浏览下这张数组的面向对象原型体系图,然后一起由这张图来分析Function和Object两大内置类的复杂关系,将数组和对象之间的两大属性__proto__和prototype的指向问题彻底理解。这张图以普通数组arr作为开端,分析Array内置类,再分析Function内置类,再分析Object内置类,最后整体分析相互之间的__proto__和prototype。
在这里插入图片描述

第一:Array()内置数组类

所有的对象都有属性__proto__ ,所有的函数都有属性prototype

//数组arr是数组Array的一个实例
let arr = [10,20,30];
arr;//控制台打印如下
/*
0: 10
1: 20
2: 30
length: 3
__proto__: Array(0)//因为arr是对象,所有的对象都有属性 __proto__
*/

//控制台打印
Array
ƒ Array() { [native code] }

Array内置数组类(所有的类都是函数类型)除了堆里存储有代码字符串之外,还有自己都属性

dir(Array);
//输出 ƒ Array() -- 展开如下
/* 
arguments: (...)
caller: (...)
from: ƒ from()
isArray: ƒ isArray()
length: 1
name: "Array"
of: ƒ of()
prototype: [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
Symbol(Symbol.species): (...)
get Symbol(Symbol.species): ƒ [Symbol.species]()
__proto__: ƒ ()
[[Scopes]]: Scopes[0]
*/
  1. length: 1 //说明支持一个形参
    如果传递的参数是一个数字则这个数字是新生成的数组的length,如果不是数字则是新数组的元素;如果传递多个参数那么不论是否为数字都是新数组的元素。
new Array(0);//=>[]
new Array(10);//=>(10) [empty × 10]
new Array(1,2,3);//=>(3) [1, 2, 3]
new Array('10');//=>["10"]
new Array('10','20','30');//=>(3) ["10", "20", "30"]
  1. Array.from()将类数组转化为数组
  • Array.from() :从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例
    Array.from(arrayLike[, mapFn[, thisArg]])
    arrayLike:想要转换成数组的伪数组对象或可迭代对象。
    mapFn 可选:如果指定了该参数,新数组中的每个元素会执行该回调函数。
    thisArg 可选:可选参数,执行回调函数 mapFn 时 this 对象。
    返回值:一个新的数组实例。
console.log(Array.from('foo'));
// Array ["f", "o", "o"]

console.log(Array.from([1, 2, 3], x => x * x));
// Array [2, 4, 6]

(function () {
    console.log(arguments);
    console.log(Array.from(arguments));
})(10, 20, 30);
//[Arguments] { '0': 10, '1': 20, '2': 30 }
//[ 10, 20, 30 ]
  1. Array.isArray()用于确定传递的值是否是一个 Array。
    Array.isArray(obj)
    obj:需要检测的值。如果返回值是 Array,则为true; 否则为false。
Array.isArray([1, 2, 3]);  // true
Array.isArray({foo: 123}); // false
Array.isArray("foobar");   // false
Array.isArray(undefined);  // false
  1. Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

Array.of(element0[, element1[, …[, elementN]]])
elementN:任意个参数,将按顺序成为返回数组中的元素。
返回值:新的 Array 实例。

Array.of(7);       // [7] 
Array.of(1, 2, 3); // [1, 2, 3]

Array(7);          // [ , , , , , , ]
Array(1, 2, 3);    // [1, 2, 3]
  1. 所有的类都是函数,所有的函数都有属性 prototype。Array数组内置类的prototype是Array.prototype,它是一个对象,这个堆内存中有健值对"constructor:Array",还有属性__proto__,包含了数组中常用的公用属性和方法:push、pop、forEach、map、sort……等。数组常用方法都在Array.prototype上。(在MDN网站可以查看这些方法)
Array.prototype;//控制台打印如下
/*
[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
concat: ƒ concat()
constructor: ƒ Array()
copyWithin: ƒ copyWithin()
entries: ƒ entries()
every: ƒ every()
fill: ƒ fill()
filter: ƒ filter()
find: ƒ find()
findIndex: ƒ findIndex()
flat: ƒ flat()
flatMap: ƒ flatMap()
forEach: ƒ forEach()
includes: ƒ includes()
indexOf: ƒ indexOf()
join: ƒ join()
keys: ƒ keys()
lastIndexOf: ƒ lastIndexOf()
length: 0
map: ƒ map()
pop: ƒ pop()
push: ƒ push()
reduce: ƒ reduce()
reduceRight: ƒ reduceRight()
reverse: ƒ reverse()
shift: ƒ shift()
slice: ƒ slice()
some: ƒ some()
sort: ƒ sort()
splice: ƒ splice()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
unshift: ƒ unshift()
values: ƒ values()
Symbol(Symbol.iterator): ƒ values()
Symbol(Symbol.unscopables): {copyWithin: true, entries: true, fill: true, find: true, findIndex: true, …}
__proto__: Object
*/
  1. 所有的函数都是对象–是Object的实例,所有的函数都是Function的一个实例,所有的对象都有属性__proto__,所有的函数都有属性prototype,所以函数既有属性__proto__又有属性prototype。
第二:Function()函数内置类(每一个函数都是它的实例)

在控制台打印Function的详细信息

dir(Function)
//输出--ƒ Function()--展开如下
/*
arguments: (...)
caller: (...)
length: 1
name: "Function"
prototype: ƒ ()
__proto__: ƒ ()
[[Scopes]]: Scopes[0]
*/
  1. length: 1 ,这个length为1如何使用?
new Function("let a=12;console.log(a);");
//返回一个匿名函数,可以将传入的参数执行
ƒ anonymous() {
let a=12;console.log(a);
}
  1. Function的原型属性: prototype = Function.prototype
Function.prototype;//打印出:ƒ () { [native code] }

dir(Function.prototype)
//打印出:ƒ anonymous()--展开如下
/*
apply: ƒ apply()
arguments: (...)
bind: ƒ bind()
call: ƒ call()
caller: (...)
constructor: ƒ Function()
length: 0
name: ""
toString: ƒ toString()
Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
get arguments: ƒ ()
set arguments: ƒ ()
get caller: ƒ ()
set caller: ƒ ()
__proto__: Object
[[FunctionLocation]]: <unknown>
[[Scopes]]: Scopes[0]
*/
  1. Function.prototype上有函数常用的公共方法:call、apply、bind、toString……
  2. 所有改变this指向的方法(call、apply、bind)都在Function的原型上
  3. Function.prototype有三个方法–call、apply、bind,每一个函数都是Function的实例,所以每一个函数的__proto__都指向Function.prototype,“每一个函数”都可以调用call、apply、bind这三个方法。
第三:Object() 对象内置类(每一个对象都是它的实例)

通过在控制台中打印查看Object中提供的方法和属性,使用时直接Object.XXX来调用Object上的属性和方法。

Object
//控制台输出:ƒ Object() { [native code] }
dir(Object)
//控制台输出:ƒ Object() -- 展开如下
/*
arguments: (...)
assign: ƒ assign()
caller: (...)
create: ƒ create()
defineProperties: ƒ defineProperties()
defineProperty: ƒ defineProperty()
entries: ƒ entries()
freeze: ƒ freeze()
fromEntries: ƒ fromEntries()
getOwnPropertyDescriptor: ƒ getOwnPropertyDescriptor()
getOwnPropertyDescriptors: ƒ getOwnPropertyDescriptors()
getOwnPropertyNames: ƒ getOwnPropertyNames()
getOwnPropertySymbols: ƒ getOwnPropertySymbols()
getPrototypeOf: ƒ getPrototypeOf()
is: ƒ is()
isExtensible: ƒ isExtensible()
isFrozen: ƒ isFrozen()
isSealed: ƒ isSealed()
keys: ƒ keys()
length: 1
name: "Object"
preventExtensions: ƒ preventExtensions()
prototype: {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
seal: ƒ seal()
setPrototypeOf: ƒ setPrototypeOf()
values: ƒ values()
__proto__: ƒ ()
[[Scopes]]: Scopes[0]
*/
  1. Object上常用的方法有: assign、create、defineProperty、entries、freeze、keys…… 在MDN网站上查看这些常用方法的用法
  2. Object的原型属性prototype=Object.prototype
    Object.prototype是一个对象,堆内存中有一个属性constructor=Object,每个对象都有属性__proto__,除了这些属性之外的其它常用属性和方法:
    hasOwnProperty、isPrototypeOf、toString、valueOf
Object.prototype
//打印出 {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}  展开如下:
/*
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
*/

2.1 hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
obj.hasOwnProperty(prop)

  • prop:要检测的属性的 String 字符串形式表示的名称,或者 Symbol。
  • 返回值:用来判断某个对象是否含有指定的属性的布尔值 Boolean。
    【描述】所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
function Func() {
    this.like = function () { }
}
Func.prototype.do = function () { }
var fn = new Func;
console.log(fn.hasOwnProperty('like'));//true
console.log(fn.hasOwnProperty('do'));//false

相关联的 IN:如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。
prop in object

  • prop:一个字符串类型或者 symbol 类型的属性名或者数组索引(非symbol类型将会强制转为字符串)。
  • objectName:检查它(或其原型链)是否包含具有指定名称的属性的对象。
    in右操作数必须是一个对象值。
var myCar = { make: "Honda", model: "Accord", year: 1998 };
console.log('make' in myCar);//true


function Func() {
    this.like = function () { }
}
Func.prototype.do = function () { }
var fn = new Func;
console.log('like' in fn);//true
console.log('do' in fn);//true

2.2 isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。

isPrototypeOf() 与 instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链__proto__是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。
eg:某构造函数Func,fn = new Func,得到一个实例fn,这个fn的__proto__=== Func.prototype,那么这个 fn instanceof Func ==> true

prototypeObj.isPrototypeOf(object)

  • object:在该对象的原型链上搜寻
  • 返回值:Boolean,表示调用对象是否在另一个对象的原型链上。
  • 报错TypeError:如果 prototypeObj 为 undefined 或 null,会抛出 TypeError。
    【描述】isPrototypeOf() 方法允许你检查一个对象是否存在于另一个对象的原型链上。
function Foo() { }
function Bar() { }
function Car() { }
//Bar.prototype.__proto__=Foo.prototype
Bar.prototype = Object.create(Foo.prototype);

//Car.prototype.__proto__=Bar.prototype
Car.prototype = Object.create(Bar.prototype);

var car = new Car();
//car.__proto__ ==> Car.prototype;
//Car.prototype.__proto__ ==> Bar.prototype;
//Bar.prototype.__proto__ ==> Foo.prototype;

console.log(Car.prototype.isPrototypeOf(car)); // true
console.log(Bar.prototype.isPrototypeOf(car)); // true
console.log(Foo.prototype.isPrototypeOf(car)); // true
console.log(Object.prototype.isPrototypeOf(car)); // true

//Car.__proto__===Function.prototype;
console.log(Bar.prototype.isPrototypeOf(Car)); //false
console.log(Car.__proto__ === Function.prototype);//true

咱们再次来看一下整体图:
在这里插入图片描述
整体图解析:

  1. arr是一个数组,所有的数组都是Array的实例,所以arr的__proto__指向数组原型Array.prototype,所以每一个数组实例都可以通过原型链找到Array原型上的方法和属性并且调用。
  2. new出来的实例对象的__proto__指向其构造函数的原型,但是像Array.prototype这样不是new出来的普通对象,它们的__proto__都指向Object的原型即Object.prototype,因为每一个对象都是Object的一个实例。
  3. Function.prototype也是一个对象,所以它的__proto__指向Object的原型即Object.prototype。
  4. 普通数组arr顺着原型链__proto__找到Array.prototype中的方法和属性,再顺着原型链__proto__找到Object.prototype中的方法和属性。
  5. 每一个对象都是Object这个内置类的一个实例,但是普通函数arr也是Object类的一个实例,但是它本身最直接属于的类是Array,所以它先是Array的实例,但是它最终也能顺着原型链找到Object.prototype上的方法。
  6. 每一个函数(或者说每一个类)都天生具有一个prototype,类的prototype属性一般是一个对象,但是有一个除外:Function.prototype是一个匿名空函数(empty/anonymous),但是它的相关操作和其它类的原型对象没有任何区别.
typeof Object.prototype;//=>"object"
typeof Array.prototype//=>"object"
typeof Function.prototype;//=>"function"
  1. Object.prototype的属性__proto__值为null
  2. 每一个函数(类)都天生自带一个属性prototype,每个函数也是对象所以也天生自带一个属性__proto__
  3. 每一个函数(包含内置类)都是Function都一个实例,所以它们的__proto__一定指向Function.prototype;每一个对象(包含原型对象等)都是Object的实例,所以它们基于__proto__一定可以找到Object.prototype;因为内置类都是函数,所以都是Function的实例,所以Array、Function、Object的__proto__都指向Function.prototype
//因为Function本身也是一个函数,所以Function也是Function的一个实例
Function instanceof Function;//=>true
Function.__proto__ === Function.prototype;//=>true
  1. 最后来看内置类函数Function和Object谁更大,Object(对象基类)是函数,所有函数都是Function的实例,所以Object的__proto__就指向Function.prototype,从这一点来看Function更大;因为每个函数也都是对象,Function的__proto__指向自己的原型Function.prototype,从Function.prototype这个对象中的属性__proto__指向Object.prototype,这样看来Object大。
    Object作为一个类(函数)它是Function的一个实例;Function虽然是函数(类),但它也是一个对象,所以它也是Object但一个实例。
Object.__proto__.__proto__ === Object.prototype;//=>true
  1. 不论是谁最后一定能找到Object.prototype,说明它们都是Object的一个实例;
    在JS中的任何实例(任何值【除了值类型的值】)最后都可以基于自己的原型链__proto__找到Object.prototype,也就是所有的值都是Object都实例 => 万物皆对象

问题:为什么Object是实例只能用Object.prototype中都方法,无法用Object都私有方法?
答:因为我们只能顺着原型链__proto__查找原型prototype上的方法,无法访问到其私有的方法。私有的方法和属性只有自己能调用。利用hasOwnProperty()方法可以区分是自己私有的,还是原型上的方法和属性。
prototype是自己私有的属性,但是prototype指向的堆中的方法和属性不是自己私有的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值