你们好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新......前端
- github:https://github.com/Daotin/Web
- 微信公众号:Web前端之巅
- 博客园:http://www.cnblogs.com/lvonve/
- CSDN:https://blog.csdn.net/lvonve/
在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目。如今就让咱们一块儿进入 Web 前端学习的冒险之旅吧!git
原型链表示的是实例对象与原型对象之间的一种关系,这种关系是经过__proto__
原型来联系的。github
实例对象的原型 __proto__
指向的是该对象的构造函数中的原型对象 prototype,若是该对象的构造函数的 prototype 指向改变了,那么实例对象中的原型 __proto__
的指向也会跟着改变。chrome
例如:微信
Person.prototype = new Cat();
由于 Person.prototype = {}; 能够是一个对象,因此传入另外一个对象的实例函数是能够的,这时候 Person 的实例对象能够访问 Cat 原型 prototype 中的属性和方法,而不能再访问本身 Person 中原型的属性和方法了。函数
实例对象的__proto__
指向的是构造函数的原型对象 prototype,因为prototype也是个对象,因此也有 __proto__
,这个 __proto__
指向的是 Object 的 prototype,而 Object 的 prototype 里面的 __proto__
指向的是 null。学习
示例:this
function Person() {} var per = new Person(); console.log(per.__proto__ === Person.prototype); // true console.log(Person.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null
原型链图示:.net
先看个案例:问下面程序有问题吗?firefox
function Person() {} Person.prototype.eat = function () {}; function Student() {} Student.prototype.say = function () {}; Student.prototype = new Person(); var stu = new Student(); stu.say();
解答:stu.say(); 会报错。由于 Student 的原型指向变成了 Person 的一个实例对象,Person 的实例对象钟并无 say 方法,因此报错。
解决办法:在原型指向改变以后再添加原型方法。
function Person() {} Person.prototype.eat = function () {}; function Student() {} Student.prototype = new Person(); Student.prototype.say = function () {}; var stu = new Student(); stu.say();
PS:这个时候就不会报错, Student 添加的原型方法的位置是一个匿名 Person 的实例对象中,这里是一个 Person 的实例对象,不是全部的,因此当你再 new 一个 Person 的实例对象的时候,不会有 say 方法。
当实例对象访问一个属性的时候,会先从实例对象中找,找到了直接使用,找不到再到指向的原型对象中找,找到了使用,仍是找不到,则为 undefined。
如何改变原型对象中的属性的值呢?怎么赋值的怎么修改。
若是你使用 对象.属性 = 值
的方式来赋值的话,若是这个属性在实例对象中有的话,改变的是实例对象中属性的值;若是实例对象中没有这个属性的话,则此次修改至关于给该实例对象添加了一个属性,其指向的原型对象中相应的属性的值并无被改变。
原型的第二个做用:继承。目的也是节省内存空间。
经过改变子类原型的指向到父类的实例对象,能够实现继承。
案例:
// 父类:人 function Person(name, age) { this.name= name; this.age=age; } Person.prototype.eat = function () { console.log("eat()"); }; // 子类:学生 function Student(sex) { this.sex=sex; } // 子类继承父类只须要改变子类原型的指向到父类的实例对象。 Student.prototype = new Person("Daotin", 18); Student.prototype.study = function () { console.log("study()"); }; var stu = new Student("male"); console.log(stu.name); // Daotin console.log(stu.age); // 18 stu.eat(); // eat()
缺陷1:在改变子类原型对象的指向的时候,属性在初始化的时候就固定了,那么每一个子类实例对象的值都固定了。
解决办法:不须要子类原型的指向到父类的实例对象,只须要借用父类的构造函数。
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log("eat()"); }; function Student(name, age, sex) { Person.call(this, name, age); this.sex = sex; } //Student.prototype = new Person("Daotin", 18); Student.prototype.study = function () { console.log("study()"); }; var stu = new Student("Daotin", 18, "male"); console.log(stu.name); console.log(stu.age); console.log(stu.sex); stu.eat(); // 不能访问
Person.call(this, name, age);
第一个参数 this,表示当前对象,意思是当前对象呼叫 Person,将 name 和 age 传过来,具体传多少,我本身指定。这样不一样的子类,经过本身能够设置不一样的属性。
缺陷2:stu.eat();
不能访问了,就是父类原型方法不能继承了。
解决办法:组合继承(原型方式继承 + 借用构造函数继承)
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log("eat()"); }; function Student(name, age, sex) { Person.call(this, name, age); // 借用父类构造函数,实现父类属性的继承 this.sex = sex; } Student.prototype = new Person(); // 不传参数了,实现原型方法的继承 Student.prototype.study = function () { console.log("study()"); }; var stu = new Student("Daotin", 18, "male"); console.log(stu.name); console.log(stu.age); console.log(stu.sex); stu.eat(); stu.study();
Student.prototype = new Person();
// 不传参数了,实现原型方法的继承。
Person.call(this, name, age);
// 借用父类构造函数,实现父类属性的继承。
就是把对象中须要共享的属性和方法直接以遍历的方式复制到了另外一个对象中。
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log("eat()"); }; var per = {}; // 循环拷贝 for(var key in Person.prototype) { per[key] = Person.prototype[key]; } console.log(per);
// 函数的声明 if(true) { function f1() { console.log("f1()--1"); } } else { function f1() { console.log("f1()--2"); } } f1();
函数声明若是放在 if-else- 里面,chrome 和 firefox 输出 f1()--1,IE8 下输出 f1()--2,由于函数声明会提早,第二个将第一个覆盖了。
// 函数表达式 var func; if(true) { func = function () { console.log("f1()--1"); }; } else { func = function () { console.log("f1()--2"); }; } func();
函数表达式的方式,输出都是 f1()--1。因此尽可能使用函数表达式。
function func() { console.log(this) // window } func();
正常状况下是证正确的。
"use strict"; function func() { console.log(this) // window } window.func(); // 严格模式下必须加 window,由于他认为函数是一个方法,方法必须经过对象来调用的。
2.一、函数也是对象,对象不必定是函数(好比:Math)。
只要有 __proto__
的就是对象;
只有要 prototype
的就是函数,由于函数才会调用 prototype 属性。
对象不必定是函数:好比 Math,中有 __proto__
,可是没有 prototype
。
2.二、全部的函数都是由 Function 构造函数建立的实例对象。
既然函数是对象,那么是什么构造函数建立的呢?
var f1 = new Function("a", "b", "return a+b"); f1(1,2); // 上面至关于:函数的声明 function f1(a, b) { return a+b; } f1(1,2); // 至关于:函数表达式 var f1 = function (a, b) { return a+b; } f1(1,2);
那么 Function 是个什么东西呢?
经查看是对象也是函数?而后查看它的 __proto__
指向的是 Object的原型对象。全部的对象指向的都是Object的原型对象。