JavaScript进阶--原型、原型链、闭包
原型
1.prototype
在JavaScript中,每个函数 都有一个prototype属性,当一个函数被用作构造函数来创建实例时,这个函数的prototype属性值会被作为原型赋值给对象实例(也就是设置 实例的__proto__
属性),也就是说,所有实例的原型引用的是函数的prototype属性。
**构造函数使用方式
function Person(name,age){
this.name = name
this.age = age
}
var p = new Person('张三',20);
每一个JavaScript对象(除了 null )都具有的一个属性,叫__proto__
,这个属性会指向该对象的原型
console.log(p.__proto__ === Person.prototype); // true
2.constructor
每个原型都有一个 constructor 属性指向关联的构造函数
console.log(Person === p.__proto__.constructor); //true
在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用
通过构造函数创建的对象,constructor 指向构造函数,而构造函数本身的constructor ,则指向Function本身,因为所有的函数都是通过**new Function()**构造的
function Person() {
}
var p = new Person()
console.log(Person.prototype); // Object{}
console.log(p.prototype); // undifined
console.log(p.constructor); //function Person(){}
此处的p是通过 Person函数构造出来的,所以p的constructor属性指向Person
console.log(Person.constructor); //function Function(){}
之前提过,每个函数其实是通过new Function()构造的
console.log({}.constructor); // function Object(){}
每个对象都是通过new Object()构造的
console.log(Object.constructor); // function Function() {}
Object也是一个函数,它是Function()构造的
console.log([].constructor); //function Array(){}
函数是对象构造的 对象也是函数构造的,俩者即是函数也是对象,所以为什么构造函数它是一个函数却返回一个对象,俩者是互相继承的关系
var o1 = new f1();
typeof o1 //"object"
prototype的用法
最主要的方法就是将属性暴露成公用的
代码对比:
function Person(name,age){
this.name = name;
this.age = age;
this.sayHello = function(){
console.log(this.name + "say hello");
}
}
var girl = new Person("bella",23);
var boy = new Person("alex",23);
console.log(girl.name); //bella
console.log(boy.name); //alex
console.log(girl.sayHello === boy.sayHello); //false
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello=function(){
console.log(this.name + "say hello");
}
var girl = new Person("bella",23);
var boy = new Person("alex",23);
console.log(girl.name); //bella
console.log(boy.name); //alex
console.log(girl.sayHello === boy.sayHello); //true
总结:
function Person(){
}
var person1=new Person()
person1.__proto__==Person.prototype
person1.constructor==Person
Person.__proto__==Function.prototype
Person.prototype.constructor==Person
person1.__proto__.constructor==Person
原型链
在js中,大部分东西都是对象,数组是对象,函数也是对象,对象更加是对象。不管我们给数组和函数定义什么内容,它们总是有一些相同的方法和属性。比如说valueOf(),toString()等
**这说明一个对象所拥有的属性不仅仅是它本身拥有的属性,它还会从其他对象中继承一些属性。当js在一个对象中找不到需要的属性时,它会到这个对象的父对象上去找,以此类推,这就构成了对象的原型链
function Foo(_name) {
this.name = _name;
}
Foo.prototype.show = function() {
console.log('I am ', this.name);
};
var f1 = new Foo('obj1');
var f2 = new Foo('obj2');
f1.show(); // I am obj1
f2.show(); // I am obj2
//我们定义的show函数在Foo.prototype中,当我们执行f1.show()时,js发现f1本身没有show这个属性,所以它就到f1的原型(也就是__proto__指向的对象)去找,找到了就可以调用
- 所有函数都有一个prototype指针,指向原型对象,如图中的Foo的prototype指针。prototype指针的意义是,当我们使用这个构造函数new出新对象的时候,新对象的
__proto__
指向prototype - 构造函数的prototype所指向的原型对象有一个constructor指针,指回构造函数。如图中Foo.prototype的constructor指针指向Foo。constructor指针有助于我们找到一个对象的构造函数是谁。
__proto__
每个对象都有,js在new一个对象的时候,会将它的__proto__
指向构造函数的prototype指向的那个对象。在上图中,f1、f2这些实例对象的__proto__
都指向了Foo.prototype。
function Foo(_name) {
this.name = _name;
}
Foo.prototype.show = function() {
console.log('I am ', this.name);
};
var f1 = new Foo('obj1');
var f2 = new Foo('obj2');
var obj = {
type:1
}
f1.__proto__ = obj
console.dir(f1)
f1.show(); // I am obj1
f2.show(); // I am obj2
Foo是一个函数,它的构造函数是js内部的function Function(),Function的prototype指向了一个对象Function.prototype,因此Foo的__proto__就指向了Function.prototype
所有的函数都以function Function()为构造函数,因此,所有函数**(包括function Function()和function Object())**的__proto__都指向Function.prototype这个对象,这个对象中定义了所有函数都共有的方法,比如call()、apply()等。
我们继续深入下去,Function.prototype这个对象,它就是一个普通的对象,它的构造函数是js内置的function Object(),function Object()的prototype指向Object.prototype,因此Function.prototype.__proto__
就指向Object.prototype,这个对象中定义了所有对象共有的属性,比如我们之前说的hasOwnProperty()和toString()等。
同理,Foo.prototype和其他自定义的对象也是
__proto__
指向Object.prototype对象
Object.prototype就是原型链的终点了,它的__proto__
是null,js查找属性时,如果到这里还没有找到,那就是undefined了
闭包
函数和函数内部能访问到的变量加在一起就是一个闭包
常规认为,一个函数嵌套另一个函数,两个函数中间的环境,叫闭包,但其实这也是制造一个不会被污染沙箱环境,实质上,由于js函数作用域的存在,所有的函数,都可以是一个闭包
function foo(){
var num = 1
function add(){
num++
return num
}
return add
}
var func = foo()
func()
闭包常常用来间接访问一个变量也可以理解为**隐藏一个变量
function foo(){
var num = 18
var obj = {
text:'我是一个字符串',
getNUm:function(){
return num
}
}
return obj
}
var obj = foo()
var age = obj.getNUm()
console.log(age)
由于 JS 的函数内部可以使用函数外部的变量,而函数外部无法访问到函数内部的变量,所以正好符合了闭包的定义。所以只要懂了 JS 的作用域,自然而然就懂了闭包。