[toc]
一.原型
1. 函数的prototype属性(图)
** 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)*
** 原型对象中有一个属性constructor, 它指向函数对象*
2. 给原型对象添加属性(一般都是方法)
** 作用: 函数的所有实例对象自动拥有原型中的属性(方法)*
<script type="text/javascript">
// 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
console.log(Date.prototype, typeof Date.prototype)
function Fun() {
}
console.log(Fun.prototype) // 默认指向一个Object空对象(没有我们的属性)
// // 原型对象中有一个属性constructor, 它指向函数对象
console.log(Date.prototype.constructor === Date)
console.log(Fun.prototype.constructor === Fun)
// //给原型对象添加属性(一般是方法) ===>实例对象可以访问
Fun.prototype.test = function () {
console.log('test()')
}
var fun = new Fun()
fun.test()
</script>
1.1 prototype(原型)和_ _ proto _ _(隐式)
在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。
1.每个函数function都有一个prototype,即显式原型(属性)
2. 每个实例对象都有一个__proto__
,可称为隐式原型(属性)
3. 对象的隐式原型的值为其对应构造函数的显式原型的值
// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype === fn.__proto__) // true
1.2 理解原型的内存图
<!--
1. 每个函数function都有一个prototype,即显式原型(属性)
2. 每个实例对象都有一个__proto__,可称为隐式原型(属性)
3. 对象的隐式原型的值为其对应构造函数的显式原型的值
4. 内存结构(图)
5. 总结:
* 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
* 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
* 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
-->
<script type="text/javascript">
//一.定义构造函数
function Fn() { // 内部语句: this.prototype = {}
}
// 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象
console.log(Fn.prototype)
// 2. 每个实例对象都有一个__proto__,可称为隐式原型
//二.创建实例对象
var fn = new Fn() // 内部语句: this.__proto__ = Fn.prototype
console.log(fn.__proto__)
// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype === fn.__proto__) // true
//三.给原型添加方法
Fn.prototype.test = function () {
console.log('test()')
}
//四.通过实例调用原型的方法
fn.test() //test()
</script>
1.3 原型的基本使用
通常我们在使用原型写代码的时候,我们都会这样做:
==1.在构造函数里面写属性。==
==2.在原型对象上写方法。==
这样做就避免了浪费内存
的问题
1.4 构造函数和实例对象,原型对象的三角关系
/*
原型:
1.是构造函数的一个属性,是一个对象,是在我们创建构造函数的时候 系统 自动分配的
作用是给 构造函数的 实例对象 提供方法
2.可以通过构造函数.prototype 得到
总结:
1. 原型对象 - 作用是给实例对象提供方法
2. 构造函数.prototype 和 实例对象.__proto__
3. 有prorotype属性是 构造函数 ,有 __proto__ 属性 就是 实例对象
*/
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
Person.prototype.sayHi = function () {
console.log('sayHi被调用了');
}
// console.dir(Person)
console.log(Person.prototype);
// 2.证明 原型的 constructor 属性 就是 构造函数
console.log(Person.prototype.constructor === Person); //true
let p1 = new Person()
let p2 = new Person()
// p1.sayHi()
// p2.sayHi()
// console.log(p1);
// console.log(p2);
//1.证明 实例对象的 __proto__ 就是 构造函数的prototype
// console.log(p1.__proto__ === p2.__proto__); //true
// console.log(p1.__proto__ === Person.prototype); //true
// console.log(p2.__proto__ === Person.prototype); //true
原型其实就是一个在浏览器中,每当创建一个构造函数,就会自动分配好的对象。
这个对象的作用,就是给实现对象共享方法用的。
我们可以通过两个方式访问到这个对象:
1.从构造函数访问原型:构造函数.prototype
2.从实例对象访问原型:实例对象.__proto__
1.5 原型总结
- 原型其实就是一个对象,存在于内存中,我们看不见
- 原型可以给构造函数的实例对象提供方法
构造函数.prototype
和实例对象.__proto__
都可以访问到原型对象- 有
prototype
属性的是构造函数,有__proto__
属性的是实例对象 - 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
- 对象的
__proto__
属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值 - 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
1.6 原型的原理
原型之所以会比单独的构造函数好,是因为它在内存这的唯一性,于是在它身上的方法也具备唯一性。
此时无论我们new多少个实例对象,它们都会在调用方法的时候,沿着它的__proto__
属性找到原型对象,然后调用原型对象上的对应的方法,因为原型对象只会有一个,所以方法对应的函数对象也就只会有一个,就解决了浪费内存的问题。
而此时在内存中,已经在多个对象之前存在这样一个关联关系
二.封装一个简单的jQuery
jQuery中就是使用了原型来实现了很多代码的封装,所以我们利用所学的原型的知识,模仿一下jQuery的代码封装。(这个过程比较复杂,我们可以先不看,在上课的时候再慢慢讲)
/* 简单的jQuery包含的功能: 1.获取元素 $(选择器) 2.注册事件 jq对象.on() 3.修改样式 jq对象.css() 目的: jq对象.on(事件类型,处理程序) jQuery.prototype.on = function(){}*/// 面向对象,为了区分对象,先 写 构造函数function jQuery(selector) { // 得到一个伪数组 var dom = document.querySelectorAll(selector);// 这是一个 NodeList 伪数组 // 我们自己造一个 for (var i = 0; i < dom.length; i++) { this[i] = dom[i]; } //伪数组都要长度 this.length = dom.length;}// 封装css方法jQuery.prototype.css = function (prop, value) { // css方法有多个用法,可以通过判断 参数个数区分 if (arguments.length === 2) { // 把 伪数组里面的所有元素都修改 // 方法里面的this,是实例对象 // this是一个伪数组,当然要遍历 for (var i = 0; i < this.length; i++) { this[i].style[prop] = value; } } else if (arguments.length === 1) { //此时 prop 应该是一个对象,需要从prop里面得到每个键值对 for (var key in prop) { for (var i = 0; i < this.length; i++) { this[i].style[key] = prop[key]; } } } // 为了支持链式编程,返回一个jq对象 return this;}// 封装on方法 - 实现注册事件jQuery.prototype.on = function (type, fn) { // 判断 当前的浏览器 是否支持 addEventListener 方法 // 事件源.addEventListener(事件类型,处理程序) for (var i = 0; i < this.length; i++) { if (typeof this[i].addEventListener === 'function') { this[i].addEventListener(type, fn); } else { // ie 的注册事件的方法 attachEvent this[i].attachEvent('on' + type, fn); } } return this}// 为了简单jQuery的使用,再包一层函数function $(selector) { return new jQuery(selector);}
三.原型链
3.1 什么是原型链?
==在JavaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。==这种一级一级的链结构就称为原型链(prototype chain)。
事实上在内存中,存在多个原型对象,多个原型对象之前存在着一个链式关系,这个链式关系我们称为:原型链
原型链是javascript特意为了实现面向对象的继承而设计的一种对象结构,这样可以解决代码的重复利用的问题。
当我们把构造函数的原型输出,再展开查看,发现原型对象上面也有__proto__
属性,也就是说其实原型对象也是一个实例对象
function Person(name,age,gender){ }console.log(Person.prototype)
而原型对象的__proto__
属性我们发现它也是一个对象,此时在内存中就至少存在这样一个链式关系
我们就称这样的结构关系为原型链
3.2 原型链的作用
我们在上方提到,==原型链是javascript专门为了实现继承而设计的==,我们先不管继承是什么,可以先看看它这样设计的用处。
前方已经学习过,原型对象的作用就为了给实例对象提供方法的,那么也就是说 对象.__proto__
上面的方法,对象就可以使用。同理,现在在原型对象(对象.__proto__
)的上面的__proto__
属性所指向的对象,它身上的方法能否被原型对象所使用呢?当然!
我们先看看在这个原型的原型上面有什么方法
console.log(Person.prototype.__proto__)
可以看到在原型的原型身上有一个toString
方法,我们尝试调用一下
console.log(Person.prototype.toString()); // 结果为: [object Object]
可见__proto__
属性身上的方法确实是可以被对象所使用的。
那么这个时候我们思考一个问题: Person构造的函数的实例对象能不能调用这个toString()
方法呢?毕竟Person实例的__proto__
属性是 Person.prototype,Person.prototype的__proto__
属性的方法也相当于是Person.prototype的方法,那么实例对象访问它的__proto__
的方法,应该也可以
let p = new Person()console.log(p.toString()); // 结果: [object Object]
可以看到,toString
方法确实通过多个对象之间的 __proto__
关系被重用了。
3.3 原型链结构
结合之前得到的Perosn与其原型之间的关系,我们可以得到一个更加完整的关系图
于是我们可以思考一个问题,Object原型对象上面还有没有__proto__
属性呢?
console.log(Object.prototype.__proto__) // null
也就是说原型链的关系到了Object原型这,再往上就没有了 ———— 原型链的尽头是 null
值得注意的是,这套东西不是我们发现的,而是js作者特意为了能够实现代码复用而设计出来的特殊结构,我们只不过是以推导的方式来带领大家学习
其实原型链就是我们在js中实现继承的基本原理
3.4 原型链成员访问规则
原型链末端的对象可以访问上游的对象的方法,其实也是有一定的访问规则的。和作用域类似的,原型链上的成员也遵循**"就近原则"**
- 读取对象的属性值时:
- 当访问一个对象的成员时,如果这个对象有自己的对应成员,优先使用自己的
- 当自己没有对应的成员时,会沿着原型链查找,找到一个最近的
- 如果直到Object.prototype身上也没有,返回undefined
-
设置对象的属性值时:==不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值==
-
==方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上==
<!-- 1. 读取对象的属性值时: 会自动到原型链中查找 2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值 3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上 --> <script type="text/javascript"> //例子一: function Fn() { } Fn.prototype.a = 'xxx' var fn1 = new Fn() console.log(fn1.a, fn1) //xxx Fn{} var fn2 = new Fn() fn2.a = 'yyy' console.log(fn1.a, fn2.a, fn2) //xxx yyy Fn{a:'yyy'} //例子二: 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) //{'Bob',12} var p2 = new Person('Jack', 12) p2.setName('Cat') console.log(p2) //{'Cat',12} console.log(p1.__proto__ === p2.__proto__) // true
3.5 原型链案例分析(==构造函数/原型/实例对象的关系(图解)==)
<!--
1. 原型链(图解)
* 访问一个对象的属性时,
* 先在自身属性中查找,找到返回
* 如果没有, 再沿着__proto__这条链向上查找, 找到返回
* 如果最终没找到, 返回undefined
* 别名: 隐式原型链
* 作用: 查找对象的属性(方法)
2. 构造函数/原型/实体对象的关系(图解)
3. 构造函数/原型/实体对象的关系2(图解)
-->
<script type="text/javascript">
// console.log(Object)
//console.log(Object.prototype)
//console.log(Object.prototype.__proto__)
function Fn() {
this.test1 = function () {
console.log('test1()')
}
}
//console.log(Fn.prototype)
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()
/*
1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
*/
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // true
/*
2. 所有函数都是Function的实例(包含Function)
*/
console.log(Function.__proto__ === Function.prototype)
/*
3. Object的原型对象是原型链尽头
*/
console.log(Object.prototype.__proto__) // null
3.6 ==构造函数/原型/实例对象的关系(图解)==
var o1=new Object();
var o2={ };
3.7 ==构造函数/原型/实例对象的关系2(图解)==
function Foo( ){ }
四.instanceOf关键字
1.instanceof是如何判断的?
表达式: A instanceof B
instance关键字规则: ==如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false==
2.Function是通过new自己产生的实例
4.1 案例一:
function Foo() {}
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true
4.2 案例二:
console.log(Object instanceof Function) // true
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
五.面试题
5.1 面试题一:熟悉以后,就不需要画图,要一眼看懂
function A() {
}
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) //2 undefined 2 3
5.2 面试题二:
function F() {}
Object.prototype.a = function () {
console.log('a()')
}
Function.prototype.b = function () {
console.log('b()')
}
var f = new F()
f.a()
f.b()//报错
F.a()
F.b()
这道题的关键在于要理解下面的终极原型链图
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!