一些基于自己的理解和总结,如有错误望指出。
1怎么理解构造函数
构造函数:所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会指向实例对象。
构造函数每次实例化都是重新构造。
<script> function Person(name,age) { this.name = name; this.age = age; this.showName = function() { alert("我的名字是" + name); } this.showAge = function() { alert("我的年龄是"+ age); } } var demo = new Person("刘德华",18); var demo1 = new Person("刘德华",18); console.log(demo.showName == demo1.showName); // false </script>
返回false是因为this的指向不一样,一个是demo,一个是demo1。
2什么是this
this:this指向的是被调用的函数的调用者(是谁调用了这个函数)和正在执行的事件的对象。
<script> function Person(name,age) { this.name = name; this.age = age; console.log(this); } Person(); // this指向window var xiaoming = new Person(); // this指向 xiaoming这个对象 </script>
3prototype和__proto__
prototype(显式原型):每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象,可以理解为prototype(显式原型)是针对于函数来说的。
__proto__(隐式原型):JavaScript中任意对象都有一个内置属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__来访问。ES5中有了对于这个内置属性标准的Get方法Object.getPrototypeOf(),可以理解为__proto__(隐式原型)是针对对象来说的。
补充: 一个对象的原型也是一个对象,Object.prototype (对象的原型对象)是个例外,它的__proto__值为null。
两者的关系和作用
关系:隐式原型指向创建这个对象的函数(constructor)的prototype。
显式原型的作用:用来实现基于原型的继承与属性的共享。
隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次查找。
4 JS中的new关键字都干了什么?
function Person() {};
var xiaoming = new Person();
new操作符具体干了什么呢,就干了三件事情:
第一,我们创建了一个空对象xiaoming
第二,我们将这个空对象的__proto__指针指向了Person函数的prototype对象。
第三,执行Person这个构造函数,如果Person有返回值(有return)直接将返回值返回给xiaoming实例对象,如果构造函数中没有返回值,就会直接把Person构造函数中的this对象返回给xiaoming实例对象,可以理解为克隆了一份Person构造函数中的this对象,给了xiaoming这个实例对象并保存在计算机内存中。
5原型继承的优点
先理解一下内存的概念
数据通过变量来管理,定义变量的过程其实就是内存分配的过程,所以本质上一切数据都是存放在内存中的,一切变量,包括对象都是在内存中管理的。
实例对象也是一个变量,他也是放在内存中的,当我们实例化一个对象,其实就是将构造函数的属性拷贝一份,同时在内存中开辟一段新的区域保存这些值。
我们定义一个构造函数是不分配内存的,只有实例化的时候才分配内存。
实例对象是如何在内存存储的,指针是什么?
当你实例化一个对象,那么内存会开辟两个内存区域:
一个保存实例的名称变量,其实保存的只是地址,也可以把它理解为指针,它指向的是地址所对应的真正数据,类似于门牌号。
另一个保存对象的真正数据。
基于上面的理论,可以来谈谈用原型继承的优点了
构造函数不论被实例化多少次,它的原型对象都只会生成一次,实例化的时候只拷贝构造函数中的属性,而不会拷贝构造函数原型对象的属性。
当我们new一个实例对象时,其中的一个作用就是让实例对象的原型指针指向构造函数的原型,所以实例对象的原型中保存的并不是从构造函数原型中继承的所有内容,而只是一个地址,一个指针。构造函数原型对象的内容并不会被拷贝一份给实例对象。这样一来,就节约了内存空间。
总而言之,构造函数实例化后,实例对象的原型都指向的是同一个原型,即是这个构造函数的原型。
通过下面这个例子理解一下:
<script> function Person(name,age) { this.name = name; this.age = age; this.showAge = function () { alert("我的年龄是"+ this.age); }; } Person.prototype.showName = function() { alert("我的名字是"+ this.name); }; var demo = new Person("刘德华",15); var demo1 = new Person("刘德华",15); alert(demo.showName === demo1.showName); // 返回true alert(demo.showAge === demo1.showAge); // 返回false </script>
实例化了两个对象demo和demo1,他们的原型都指向的构造函数Person的原型,但是this的指向却是不同的。
6属性屏蔽理论
因为当我们调用一个函数的方法和属性时,先在函数本身里面寻找,找不到就会在函数的原型对象里寻找,如果原型对象中和构造函数中有相同的属性的话,就会显示构造函数中的属性。
我们想访问原型中的被屏蔽掉的属性有两种方法:
1.delete
2.使用Product.prototype.属性或者方法名称
function Product(){ this.name=‘神仙‘; this.description = ‘‘ this.buy = function(){ alert(‘构造函数对象‘) } } Product.prototype={ name:‘魔鬼‘, buy:function(){ alert(‘原型对象‘) } } var product = new Product() console.log(product.name) // 神仙 /*清除构造函数中的name*/ delete product.name // 清除后显示原型对象中的name属性 console.log(product.name) // 魔鬼 product.name=‘魔鬼2‘ // 再次给name赋值 console.log(product.name) // 魔鬼2 /*没有清除之前,也可以用这种Product.prototype.buy()式来获取原型方法*/ product.buy(); // 构造函数对象 Product.prototype.buy(); // 原型对象 console.log(product.buy) // function (){alert(‘构造函数对象‘)} /*清除*/ delete product.buy console.log(product.buy) //function (){alert(‘原型对象‘)} product.buy(); // 原型对象
持续补充...