js函数高级:03、详解原型与原型链(原型、显式原型与隐式原型、原型链、原型链属性、探索instanceof、案例图解)及相关面试题

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.prototypefn.__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

相关推荐
烛阴1 小时前
C#继承与多态全解析,让你的对象“活”起来
前端·c#
狗哥哥1 小时前
Swagger对接MCP服务:赋能AI编码的高效落地指南
前端·后端
zl_vslam1 小时前
SLAM中的非线性优-3D图优化之相对位姿Between Factor(六)
前端·人工智能·算法·计算机视觉·slam se2 非线性优化
申阳1 小时前
Day 18:01. 基于 SpringBoot4 开发后台管理系统-快速了解一下 SpringBoot4 新特性
前端·后端·程序员
500佰1 小时前
技术包办模式给我带来的反思
前端
g***72701 小时前
spring-boot-starter和spring-boot-starter-web的关联
前端
用户41429296072391 小时前
解决「买不到、买得贵、买得慢」:反向海淘独立站的核心功能设计与案例复盘
前端·后端·架构
N***p3651 小时前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
五号厂房1 小时前
网络请求库通用封装(GET/POST + 超时 + 传参)+fetch
前端