学习参考: MDN
前言:
先前Niao(说的就是我啦)一直从事着js学习研究,当时我是跟着廖老师的es系列教程走的。打算在毕业前搞个小东东,迫于时间问题。教程仅仅过了个大概。之前买的js进阶书籍就一直凉着。为了完全吃透js。niao也是下定决心,未来的一段时间,一直会更新js的进阶学习总结。
函数创建
先说明:js中一切皆对象,就连函数也不列外,不要看函数怎么跟一般的对象不一样呢,其实这是js中特殊对象。其实函数也有对象的方法和属性,比如
//声明一个函数foo
funciton foo()
{
console.log(1314520)
}
//调用foo
foo()//1314520没毛病
//接下来才是重点,调用protoype
foo.prototype//-->指向原型对象
可以确定函数也是对象,也有对象该有的属性方法了。回到开始,函数创建过程,申明一个函数,接下来js引擎会调用构造函数Funciton创建该函数。所以foo.constructor === Funciton(foo.constructor函数本身没有这个属性),所用函数的constructor均指向Function。接下来js引擎还会在内存中创建该函数原型对象,这个是默认的。该对象,通过prototype来引用。
对象创建
** 对象创建几种方式**
- 字面量方式:
举个栗子:
let obj = {}//这里obj是变量,{}就是字面量
这种方式同样是被 - new调用构造函数创建
new调用构造创建对象所起作用,这里说明一下,
1.首相会创建一个实列对象
2.然后把实列对象的__proto__指向构造函数的原型对象
3.然后改变构造函数内的this指向。
4.最后返回该对象
这里写个封装new函数,实现底层
function New(func){
let obj = {}; //这里就直接新建一个空对象,用于返回的新对象。
if(func.prototype != null){
obj.__proto__ = func.prototype;
}
//改变this指向
func.apply(obj,Array.prototype.slice.call(arguments, 1));
//把func构造函数里的this 指向obj对象,把传进来的第二个参数开始,放入func构造函数里执行,比如:属性赋值。
//需要属性赋值的构造函数,如function foo(a1,a2,....)
return obj;
}
- Object.create(obj,[options])该方法是ES5新增方法。该方法会新创建一个对象,它会把传入函数或对象关联到新建对象的__proto__。
create实现底层,这里介绍几个方法:
function create (obj,[options])//options:传入对象,外部对象里面是各个属性的属性描述符(数据描述符)
{
let obj = {};
obj.__proto__ = obj
if (options === null){
return obj;
}
return Object.defineProperties(obj,options)//如果需要添加需要属性,可以传入值
}
2.兼容性写法,因为__proto__为非标准属性,仅兼容市面上大多数主流浏览器,FF,chrome,… 。像IE这种古董,怕是得折腾人了,这里就不作测试- _ - 。
function create (obj)
{
function f () {};//这里得f做过度用,是个中间量。用于临时存储新对象__proto__ ==> obj
f.prototype = obj
return new f()
}
上面这个方法,实现的效果和方法一相同。但不理解new底层的,理解起来还是有点突兀的。这个可以结合上面我讲的new的作用,以及new函数封装。
这里我在变换一下写法:
function create(proto){
let f = function(){};
let obj = {};
f.prototype = proto;
obj.__proto__ = f.prototype;
return obj;
}
测试上面的方法:
这下没得问题了巴 ??
原型链
原型链,这里我用图表示:
说明: 理解此图内容,可以结合下面原型指向内容
说起原型链不得不说两个重要属性:
protitype,proto.他们是访问原型入口。区分如下:
prototype:函数独有,其他实列对象不能访问得到
proto:所有对象都有该属性,即函数也能访问。
注意 : __prototype并不是一个标准属性,仅得到主流浏览器支持。标准属性为[[prototype]]
proto
上面属性就是访问原型的属性,为非标准属性,标准属性[[prototype]],隐藏属性,访问不到。
protoype
函数特有属性,用于访问函数原型对象。对象就两种,普通对象(字面量和new),原型对象。
区别:
每个对象都有原型,通过__proto__访问,对象内有实列继承成员,而prototype为函数的原型对象,上面定义了他的实列继承的相关。可以自定义属性方法,供原型链下游继承。
原型。什么是原型呢?
js是基于原型语言,而每一个对象,都有模板(原型对象),用于对象的属性和方法继承。而原型对象对象也有原型,最终指向Object.prototype.proto = null. 层层继承,就形成一条完整的链,原型链,而所说的原型就是原型对象。
下面看一个实列,理解原型以及原型链的运行原理,内涵
创建函数
funciton foo () {}
//在函数原型上添加属性
foo.prototype.name = 18
console.log(foo.prototype)
结果:
{
foo: “bar”,
constructor: ƒ doSomething(),
proto: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
在是通过函数foo,new一个实列化对象
let obj = new foo()
//添加属性
obj.age = 110
console.log(obj)
结果:
{
age: 110,
proto: {
name: 18,
constructor: ƒ doSomething(),
proto: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
}
这里通过对象属性访问,同样可以访问到obj.__proto__上的name.提示:obj.proto === foo.prototype.基于原型,对象obj从函数原型上继承了属性name.这就是原型的使用。
原型链的访问过程分析:
对于属性name访问,浏览器首相查找obj对象内是否有此属性,如果没有。继续溯源原型链。在obj.proto(foo.prototype)查找,查找到输出;否则,继续像上层原型排查,proto__的__proto,这时的原型对象指向Object.prototype.因函数默认的prototype的__proto__指向Object.prototype,在此原型上查找该属性,如果不存在。继续溯源原型链,即obj.proto__的__proto__的__proto.然而是不存在的,因为Object.prototype.proto = null.此时原型链已经遍历完。该属性被复制为undefined
注意:实列对象和原型对象对原型的访问,只能通过__proto__,必须重申。原型链中的方法和属性没有被复制到其他对象——它们被访问需要通过前面所说的“原型链”的方式。
对象继承的属性,都被存放在原型,prototype内(称之为子命名空间)
原型指向
说完原型及原型链,再来总结具体原型的指向,总的如下:
范对象,还有js的特殊对象,函数。这里我分开总结,有函数搅和,整的太乱了。
1.函数的prototype指向原型对象,该对象由此函数构造(new)。该对象的__proto__指向Object.prototype;函数__proto__指向原型Function.prototype。应为函数也是对象,故也有constructor对应Function函数的构造,原型__proto__的__proto__指向Object.prototype
2.范对象(实列对象):
实列prototype===>构造的.prototype,非实列的成员均继承该构造的原型(当然该构造的prototype != null),那么该构造的__proto__ ===>Object.prototype
还有一点需要说明:对于Object.create创建的新对象的__proto__指向给定的实列。
这点可以参考上面new以及create的封装。??
Constructor
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。对于函数来所,函数的原型对象prototype以及constructor两个属性紧密联系。此属性是原型对象特有,因此应用此构造需要遍历原型链。此又可作为原型链理解的案列。
此属性的运用:
代码:
function Person(name,age,interests) {
this.name = name;
this.age = age;
this.interests = interests
}
let person1 = new Person('keke','18',['girls','swiming'])
//使用constructor,应用Person. 这里constructor继承自。Person,因此可以直接调用,而不用加.prototype
let person2 = person1.constructor('tom','8',['fish','pair'])
//访问新对象属性
console.log(person2.name)//tom
原型链运用
1.属性查找(而部分有讲)
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
2.对象继承
function Person(name){ //父类(构造函数)
this.name = name;
}
Person.prototype.getName = function(){ //在父类的原型对象上添加方法
return this.name;
}
function Son(name,age){ //子类(构造函数)
this.name = Person.call(this, name);
this.age = age;
}
Son.prototype = new Person(); //把子类(构造函数) 的原型对象 挂到 原型链上去。
Son.prototype.getAge = function(){
return this.age;
}
var s = new Son("zdx",666);
s.getName(); //Son.prototype 上没有 getName方法,现在能使用了,就完成了 继承。
son.prototype = new Person('')