前言
有些新手朋友可能听说过这么一句话,就是js
中存在两个链条,它们分别为:作用域链
和原型链
它们彼此的区别在于作用域链
是为了访问变量
和数据
而存在的一种链条访问机制
而原型链
是访问对象
的属性
或者方法
而存在的一种机制!
其中这里的原型链
就是今天我要说的主题!
我们学习js
必须要知道什么是原型、原型链、构成函数、实例对象
这些彼此之间的关系和应用范围!
如果你没有搞明白js
中的原型链
也就说明你没有把js
学明白!
那么接下来就跟着我一起开始学习吧!
为什么要使用原型?
原型
在js
中也称之为原型模式
, 那么为什么要有这种模式存在呢?
因为我们js面向对象
不是说过吗?面向对象编程
其实就是一种模块、一种封装
对吧!
如果你还没有完全明白javascript面向对象
建议你一定要听我讲完下面的知识点!
构造函数模式
有的时候我们会使用构造函数模式
来创建对象
虽然这种构造函数
创建对象
,看起来没什么大问题,但是并非没有缺点!
这里我们来看一段简单的构造函数模式
创建对象的案例吧!
代码
function Person(username,age,job){
this.username=username;
this.age=age;
this.job=job;
this.say=function (){
console.log('我的名字叫:'+this.username+',年龄:'+this.age+',职业:'+this.job);
}
}
var test=new Person('张三',18,'ui设计师');
test.say();
这就是一段非常简单的构造函数模式
封装对象的方法!
构造函数模式的缺点
任何一种技术的出现,都是为了弥补旧技术的不足!
那么构造函数模式
有什么缺点呢?为什么又要搞出一个原型模式
呢? 搞出这么个东西意义何在?
这里我先卖个关子,想知道的话就继续往下看吧! 😛😛😛
从上面的代码角度上看,确实感觉不出什么奇怪之处!
那我们再来看一个构造函数模式
的例子:
代码
//构造函数
function Person(){
this.say=function (num){
console.log('测试方法'+num);
}
}
//实例化
var obj_1=new Person();
var obj_2=new Person();
//调用
obj_1.say(1);
obj_2.say(2);
console.log(obj_1.say===obj_2.say); //返回false
然后我们看看下面这张图你就知道其中缺点在什么地方了!
如图
此时你会发现,这个函数居然会不相等!! 为什么呢?
从另一个角度来讲构造函数
每执行一次就会把构造函数
中的方法
也重新在内存中生成
一份相同的方法!
比如: 执行一万次构造函数,那么就会在内存中创建一万次构造函数里面的方法, 这对内存是一种非常大的消耗
要知道在ECMAScript
中只要是函数
都是对象
这句话!
之所以会返回false
就是因为函数的底层地址是不一样的! 不要以为方法名称一样就是一样! 那你就错了
如图
并且函数本身也是对象, 你定义一个函数,也就相当于实例化了一个函数对象
, 其实就会在内存中开辟一个空间,地址也会不一样!
上面的这行代码
this.say=function (num){
console.log('测试方法'+num);
}
其实从一定逻辑上讲也可以看成以下形式
this.say=new Function (num){
console.log('测试方法'+num);
}
这样子其实你更好理解每个构造函数
实例中的方法,其实是不同Function
的实例
所以说像这种在内存中无限创建很多完成同样方法的Function实例
是完全没有必要的!
那么有没有什么好的方案可以解决这个问题呢?
我们完全可以使所有的对象
共享同一个方法,那么构造函数
执行一万次,在内存中也只会存在一份相应的方法!
通常是可以把这个函数方法
转移到构造函数
外部
代码说明
function Person(username,age,city){
this.name=username;
this.age=age;
this.city=city;
this.say=say;
/*this.say=function() {
console.log(this.name+'的年龄是:'+this.age);
}*/
}
//把say方法写在全局作用域中
function say() {
//这里的this要清楚是谁在调用say这个方法 this自然就指向谁
console.log(this.name+'的年龄是:'+this.age);
}
var test1=new Person('张三',33,'北京市');
var test2=new Person('李四',66,'深圳市');
//判断test1与test2之间的方法是否是共用的
console.log(test1.say==test2.say);
如图
案例2
以下案例也是同样的道理!
//构造函数
function Person(){
this.say=say;
}
//定义到全局下
function say(num){
console.log('测试方法'+num);
}
//实例化
var obj_1=new Person();
var obj_2=new Person();
//调用
obj_1.say(1);
obj_2.say(2);
console.log(obj_1.say===obj_2.say); //返回true
目的我们是达到了,但是这样写就真的行了吗? 不会存在其他问题吗?
分析
很明显在开发当中 如果把函数这样子写在全局作用域中,在多人开发的时候那么就会出现命名冲突,或者说这样做会 污染全局作用域的命名空间, 所以说我们在项目开发的时候都是尽量地不在全局作用域中写变量和函数
那么如何来解决这样的问题呢? 就是接下来我要说的原型和原型链
,然后根据情况来解决相应的问题!