javascript 的原型、显式原型与隐式原型、原型链、原型链属性、探索instanceof及案例图解:
Ⅰ、原型:prototype
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>01_原型(prototype)</title>
</head>
<body>
<!--
1、函数的 prototype 属性(图):
* 每个函数都有一个 prototype 属性,它默认指向一个 Object 空对象(即称为:原型对象)
* 原型对象中有一个属性 constructor,它指向函数对象;
2、给原型对象添加属性(一般都是方法):
* 作用:函数的所有实例对象自动拥有原型中的属性(方法)
-->
<script type="text/javascript">
//每个函数都有一个 prototype 属性,它默认指向一个 Object 空对象(即称为:原型对象)
console.log(Date.prototype, typeof Date.prototype)// Object 对象(里面有很多供实例使用的方法),'object'
// 原型上面的方法是给实例对象使用的;
function fun() {
}
console.log(fun.prototype)// {}(即:默认指向一个 Object 空对象(没有我们的属性))
fun.prototype.test = function () {
console.log('test()')
}
console.log(fun.prototype)// {test: ƒ}(即:添加了一个自己的属性 test 函数)
//原型对象中有一个属性 constructor,它指向函数对象;
console.log(Date.prototype.constructor)// ƒ Date() { [native code] }
console.log(Date.prototype.constructor === Date)// true
console.log(fun.prototype.constructor === fun)// true
function Fun() {
}
// 给原型对象添加属性(一般是方法) ===> 实例对象可以访问
Fun.prototype.test = function () {
console.log('test()')
}
var fun = new Fun()
fun.test()// 'test()',(注意:此时是调用了 test 函数后的输出结果值)
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

Ⅱ、显式原型与隐式原型:prototype、__proto__
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>02_显式原型与隐式原型</title>
</head>
<body>
<!--
1、每个函数 function 都有一个 prototype,即:显式原型(属性);
2、每个实例对象都有一个 __proto__,可称为隐式原型(属性);
3、对象的隐式原型的值为其对应构造函数的显式原型的值;
4、内存结构(图):
5、总结:
* 函数的 prototype 属性:在定义函数时自动添加的,默认值是一个空 Object 对象;
* 对象的 __proto__ 属性:创建函数时自动添加的,默认值为构造函数的 prototype 属性值;
* 程序员能直接操作显示原型,但不能直接操作隐式原型(ES6 之前)
6、测试过程(*):
* 定义构造函数;
* 创建实例对象;
* 给原型添加方法;
* 通过实例调用原型的方法;
-->
<script type="text/javascript">
// 定义构造函数;
function Fn() {// 内部语句:this.prototype = {}
}
// 1、每个函数 function 都有一个 prototype,即:显式原型属性,默认指向一个空的 Object 对象;
console.log(Fn.prototype)// {}(即:Object 对象);
// Fn 的 prototype 属性,是在函数对象一创建的时候就加进去了,而函数对象是在定义的时候就创建了,因此 Fn 的 prototype 属性是在定义函数时自动添加的;
// 2、每个实例对象都有一个 __proto__,可称为隐式原型;
// 创建实例对象;
var fn = new Fn()// 内部语句:this.__proto__ = Fn.prototype
console.log(fn.__proto__)// {}(即:Object 对象);
// fn 的 __proto__ 属性,是在创建函数对象(即:new Fn())的过程中添加的,因此 fn 的 __proto__ 属性是在创建函数时自动添加的;
// 3、对象的隐式原型的值为其对应构造函数的显式原型的值;
console.log(Fn.prototype === fn.__proto__)// true
// 给原型添加方法:
Fn.prototype.test = function() {
console.log('test()')
}
// 通过实例调用原型的方法:
fn.test()// 'test()'
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

其三、显示原型与隐式原型图解:

其四、显示原型与隐式原型代码图解:

Ⅲ、原型链:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>03_原型链</title>
</head>
<body>
<!--
1、原型链(图解):
* 访问一个对象的属性时,
* 先在自身属性中查找,找到返回;
* 如果没有,再沿着 __proto__ 这条链向上查找,找到返回;
* 如果最终没找到,返回 undefined;
* 别名:隐式原型链;
* 作用:查找对象的属性(方法);
2、构造函数/原型/实例对象的关系(图解)
3、构造函数/原型/实例对象的关系2(图解)
4、细则事项:
* 所有函数的 __proto__ 都是一样的(原因:都是通过 var foo = new Function() 的方式产生的,因此所有函数的 __proto__ 都是一样的),
* 唯一含有显式原型与隐式原型且指向同一原型对象的是构造函数(原因:通过 Fun = new Function() 的方式产生的,因此其既有 prototype 属性又有 __proto__ 属性,且都指向函数的原型对象)
-->
<!--
1、函数的显式原型指向的对象默认是空 Object 实例对象(但 Object 不满足);
// 因为 Object 既是函数实例对象也是构造函数对象,函数实例对象只有 __proto__ 属性(Object.__proto__)指向空 Object 实例对象,
// 构造函数对象只有 prototype 属性(Object.prototype)指向 Object 的原型对象,此时就是原型链的尽头(即:Object.prototype.__proto__ 为 null);
2、所有函数都是 Function 的实例,包括 Function 函数本身(即:Function 是它自身的实例);
3、Object 的原型对象是原型链尽头;
-->
<script type="text/javascript">
// console.log(Object.prototype.__proto__)// null(即:表示是原型链尽头,返回值为 null)
console.log(Object)// ƒ Object() { [native code] },(即:原本就存在的 Object 全局对象)
console.log(Object.prototype)// Object 的原型对象:{__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, ...}
// Object 原型对象的方法:constructor()、hasOwnProperty()(含义:是否有自定义的属性)、isPrototypeOf()、propertyIsEnumerable()、toLocaleString()、toString()、valueOf();
console.log(Object.prototype.__proto__)// null(即:表示是原型链尽头,返回值为 null)
console.log(Object.prototype.prototype)// undefined(即:表示没有)
// 突发奇想的测试:
console.log(Object.__proto__)// ƒ () { [native code] }
console.log(Object.__proto__.__proto__)// Object 的原型对象:{__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, ...}
console.log(Object.__proto__.__proto__.__proto__)// null
console.log(Object.__proto__.prototype)// undefined(即:表示没有,Object 空对象没有 prototype 属性值)
console.log(Object.__proto__.__proto__.prototype)// undefined
// console.log(Function.prototype.__proto__.__proto__)// null
console.log(Function)// ƒ Function() { [native code] }
console.log(Function.prototype)// ƒ () { [native code] }
console.log(Function.__proto__)// ƒ () { [native code] }
console.log(Function.prototype === Function.__proto__)// true
console.log(Function.prototype.__proto__)// 也就是Object 的原型对象:{__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, ...}
console.log(Function.prototype.__proto__.__proto__)// null
console.log(Function.prototype.__proto__.prototype)// undefined(即:表示没有)
console.log(Function.prototype.prototype)// undefined(即:表示没有,Object 空对象没有 prototype 属性值)
function Fn() {
this.test1 = function() {
console.log('test1()')
}
}
console.log(Fn.prototype)// ƒ () { test2: ƒ () } (注意:非 Object 空对象,也非 Function 的 prototype 值,其就是 Fn 的 prototype)
console.log(Fn.prototype === Function.prototype)// false
console.log(Fn.__proto__ === Function.prototype)// true
console.log(Function.__proto__ === Function.prototype)// true
Fn.prototype.test2 = function() {
console.log('test2()')
}
var fn = new Fn()
fn.test1()// test1()
fn.test2()// test2()
console.log(fn.toString())// [object Object]
console.log(fn.test3)// undefined
// fn.test3()// Uncaught TypeError: fn.test3 is not a function,因为 fn.test3 的值是 undefined,因此 fn.test3() 会报错;
// console.log(fn.__proto__ === Fn.prototype) // true
/*
1、函数的显式原型指向的对象默认是空 Object 实例对象(但 Object 不满足),
*/
console.log(Fn.prototype instanceof Object)// true(说明:函数的显式原型是 Object 实例对象)
console.log(Object.prototype instanceof Object)// false(说明:Object 的显式原型不是 Object 实例对象)
console.log(Function.prototype instanceof Object)// true(说明:Function 的显式原型是 Object 实例对象)
/*
2、所有函数都是 Function 的实例,包括 Function 函数本身(即:Function 是它自身的实例)
*/
console.log(Function.__proto__ === Function.prototype)// true
/*
3、Object 的原型对象是原型链尽头
*/
console.log(Object.prototype.__proto__)// null
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

其三、原型与原型链_原型链Object图解:
// 注意:此时 Fn.prototype 指向并不是 Object 空对象,图标有误;

其四、原型与原型链_原型链代码图解:
// 注意:此时 Fn.prototype 及 fn.__proto__ 指向的是同一个对象,但并不是 Object 空对象,图标有误;

Ⅳ、原型链属性问题:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>04_原型链_属性问题</title>
</head>
<body>
<!--
1、读取对象的属性值时:会自动到原型链中查找;
2、设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值;
3、方法一般定义在原型链中,属性一般通过构造函数定义在对象本身上;
-->
<script type="text/javascript">
function Fn() {
}
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a)// 'xxx',原型对象上面的属性,实例对象自动可见;
console.log(fn1)// Fn {},但原型对象中有 a 属性,且值为 'xxx'
var fn2 = new Fn()
fn2.a = 'yyy'
console.log(fn1.a)// 'xxx',
console.log(fn2.a)// 'yyy'
console.log(fn2)// Fn {a: 'yyy'},且原型对象中有 a 属性,且值为 'xxx'
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function(name) {
this.name = name
}
var p1 = new Person('Tom', 12)
p1.setName('Bob')
console.log(p1)// Person {name: 'Bob', age: 12}, 且原型对象中有 setName 方法;
console.log(p1.name)// 'Bob'
console.log(p1.age)// 12
var p2 = new Person('Jack', 12)
p2.setName('Cat')
console.log(p2)// Person {name: 'Cat', age: 12}, 且原型对象中有 setName 方法;
console.log(p2.name)// 'Cat'
console.log(p2.age)// 12
console.log(p1.__proto__ === p2.__proto__)// true
//(原因:对象的隐式原型的值为其对应构造函数的显式原型的值,因为 p1 对象和 p2 对象的构造函数是同一个,因此该构造函数的显式原型的值是同一个,因此该 p1 对象和 p2 对象的隐式原型的值是同一个)
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

Ⅴ、探索instanceof:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>05_探索instanceof</title>
</head>
<body>
<!--
1、instanceof 是如何判断的?
* 表达式:A instanceof B(即:A 是实例对象,B 是构造函数对象,而构造函数具有显式原型属性(prototype),而实例对象具有隐式原型属性(__proto__))
* 如果 B 函数的显式原型对象在 A 对象的原型链上,返回 true,否则返回 false;
2、Function 是通过 new 自己产生的实例;
-->
<script type="text/javascript">
/*
案例1:
*/
function Foo() {}
var f1 = new Foo()
console.log(f1 instanceof Foo)// true
console.log(f1 instanceof Object)// true
/*
案例2:
*/
console.log(Function)// ƒ Function() { [native code] }(即:是 Function 函数)
// 同理:Function 既是 A(函数实例:Function 函数实例,也就是 Function 函数实例对象) 也是 B(构造函数:Function 构造函数,也就是 Function 函数对象);
// 在 A 的情况下:A.__proto__(即:Object 空对象),再通过 A.__proto__.__proto__ 访问的是 Object 的原型对象,此时就是原型链的尽头(即:A.__proto__.__proto__.__proto__ 为 null);
// 在 B 的情况下:B.prototype(即:Object 空对象),再通过 B.prototype.__proto__ 访问的是 Object 的原型对象,此时就是原型链的尽头(即:A.__proto__.__proto__.__proto__ 为 null);
console.log(Function.__proto__)// ƒ () { [native code] }(即:Object 空对象,也就是 Object 空的函数实例(对象))
console.log(Function.__proto__.__proto__)// Object 的原型对象
console.log(Function.__proto__.__proto__.__proto__)// null
console.log(Function.prototype)// ƒ () { [native code] }(即:Object 空对象,也就是 Object 空的函数实例(对象))
console.log(Function.prototype.__proto__)// Object 的原型对象
console.log(Function.prototype.__proto__.__proto__)// null
console.log(Object)// ƒ Object() { [native code] }(即:是 Object 函数)
// Object 既是 A(函数实例:Object 函数实例,也就是 Object 函数实例对象) 也是 B(构造函数:Object 构造函数,也就是 Object 函数对象);
// 在 A 的情况下:A.__proto__(即:Object 空对象),再通过 A.__proto__.__proto__ 访问的是 Object 的原型对象,此时就是原型链的尽头(即:A.__proto__.__proto__.__proto__ 为 null);
// 在 B 的情况下:B.prototype(即:Object 的原型对象),此时就是原型链的尽头(即:B.prototype.__proto__ 为 null);
console.log(Object.__proto__)// ƒ () { [native code] }(即:Object 空对象,也就是 Object 空的函数实例(对象))
console.log(Object.__proto__.__proto__)// Object 的原型对象
console.log(Object.__proto__.__proto__.__proto__)// null
console.log(Object.prototype)// Object 的原型对象
console.log(Object.prototype.__proto__)// null
console.log(Object instanceof Function)// true(即:Object 是函数类型)
console.log(Object instanceof Object)// true
console.log(Function instanceof Function)// true
console.log(Function instanceof Object)// true
function Foo() {}
console.log(Object instanceof Foo)// false
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

其三、原型与原型链_instanceof案例(全)图解:

Ⅵ、相关面试题:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>06_面试题</title>
</head>
<body>
<script type="text/javascript">
/*
测试题1
*/
var A = function() {
}
A.prototype.n = 1
var b = new A()
A.prototype = {
n: 2,
m: 3
}
var c = new A()
console.log(b.n, b.m, c.n, c.m)// 1, undefined, 2, 3(可以通过画图来运行代码解释)
/* 测试题2 */
function F(){}
Object.prototype.a = function() {
console.log('a()')
}
Function.prototype.b = function() {
console.log('b()')
}
var f = new F()
// 注意:函数实例对象的原型与函数构造函数对象的原型指向是两码事,是两个不同的对象,但其 __proto__ 指向的都是 Object.prototype;
// 因此:F.b 能拿到 b 函数并执行,而 f.b 报错;
f.a()// a()
// f.b()// f.b is not a function
F.a()// a()
F.b()// b()(此时执行 F.a() 语句,就说明是把 F 当成了函数实例对象,而非构造函数,切记要区分清楚)
console.log(f)// F {}
console.log(Object.prototype)// 里面有 a 方法;
console.log(Function.prototype)// ƒ () { [native code] }(里面有 b 方法,但是私有代码不让看)
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

其三、原型与原型链_例题1图解:

Ⅶ、小结:
其一、哪里有不对或不合适的地方,还请大佬们多多指点和交流!
其二、若有转发或引用本文章内容,请注明本博客地址(直接点击下面 url 跳转) https://blog.csdn.net/weixin_43405300,创作不易,且行且珍惜!
其三、有兴趣的话,可以多多关注这个专栏(Vue(Vue2+Vue3)面试必备专栏)(直接点击下面 url 跳转):https://blog.csdn.net/weixin_43405300/category_11525646.html?spm=1001.2014.3001.5482
其四、再有兴趣的话,也可以多多关注这个专栏(Java)(直接点击下面 url 跳转):https://blog.csdn.net/weixin_43405300/category_12654744.html?spm=1001.2014.3001.5482