目录
1. 原型是在构造函数之上的,又是构造函数的一个属性
一个构造函数的原型属性,是这个构造函数所有实例化出来的对象的公共祖先,所有构造函数构造出来的对象,都继承于它的原型
function Person(){
}
Person.prototype.name = 'Lucy';
var p = new Person();
console.log(p);
// 实例化出来的对象是完全继承于它的原型的
2. 原型 -> 继承 ⭐
Professor.prototype = {
name: 'Tom',
tSkill: 'JAVA'
}
function Professor(){}
var professor = new Professor();
// ---------------------------------
Teacher.prototype = professor;
function Teacher(){
this.name = 'Joe';
this.mSkill = 'JS/JQ';
}
var teacher = new Teacher();
console.log(teacher);
//------------------------------
Student.prototype = teacher;
function Student(){
this.name = 'Lily';
this.pSkill = 'HTML/CSS';
}
var student = new Student(); // student继承了上面所有原型的属性
console.log(student);
-
原型链继承 -> 存在引用值共享的问题
-
借用构造函数 -> 父类原型方法无法获取(覆盖了)
-
组合继承(伪经典继承) -> 上面两个同时使用,会出现构造函数执行两次的情况
-
寄生组合继承(经典继承): YUI
-
圣杯模式
-
es6 class 继承
-
拷贝继承 -> 不常用,知道有就行
问:原型链继承存在的问题:引用值共享的问题,如何解决?为什么?⭐
答:借用构造函数解决,-> 会出现父类原型方法无法获取的问题 ⭐
原型链继承的时候,拿的是原型上的属性,原型的属性是继承而来,实例是没有办法修改原型的属性,但是只能够保证引用是对的,共享的是引用,所以修改了之后所有后代的值都会变。
借用构造函数之后,sub1和sub2拿的并不是原型上的属性,是成员属性,是构造函数自身的属性,每一次实例,都会执行一次,不会对其它实例有影响,每一次实例都是独立的
function Super(){
this.a = [1, 2, 3, 4];
}
Super.prototype.say = function(){
console.log(2);
}
function Sub(){
Super.call(this); // 构造函数自身的属性,每一次实例都是独立的
}
// Sub.prototype = new Super(); // 原型链继承
Sub.prototype.say2 = function(){
console.log(33333);
}
// 经典继承
// Object.create 是 es5 的方法, es3可能不兼容
if(!Object.create){ // 如果这个方法不存在,重写
Object.create = function(proto) {
var F = function(){};
F.prototype = proto;
return new F(); // 返回的是一个实例
}
}
// 经典继承
Sub.prototype = Object.create(Super.prototype);
// 相当于 Sub.prototype.__proto__ = Super.prototype
// 实例Sub.prototype的原型是Super.prototype
// 但因为 es6 不推荐 __proto__ 这种写法,所以用Object.create
// 因为没必要拿成员方法,所以直接继承原型
// 重写了Sub.prototype,原有的Sub.prototype上的属性会丢失
// -> 可以将方法写在 继承之后
var sub1 = new Sub();
var sub2 = new Sub();
sub1.a.push(5);
console.log(sub1.a);
console.log(sub2.a);
es6
class Super{
constructor(){
this.a = [1, 2,3,4];
}
say2(){
console.log(2222);
}
}
class Sub extends Super {
// 静态函数的写法
static say1() {
console.log('1111');
}
// 原型上的方法法
say(){}
}
// 上面这个方法相当于
// Sub.say1 = function(){}
var sub1 = new Sub();
var sub2 = new Sub();
// sub1.a = '3333';
sub1.a.push(5);
console.log(sub1.a);
console.log(sub2.a);
sub1.say1();
sub1.say2();
//----------------
// 静态方法
Array.from();
Object.getPrototypeof();
// 实例方法
[].sort()
3. call、apply
通过 apply 借用别人的属性和方法
Teacher.prototype.wife = 'Jane';
function Teacher(name, mSkill){
this.name = name;
this.mSkill = mSkill;
}
function Student(name, mSkill, age, major){
Teacher.apply(this, [name, mSkill]); // 借用 Teacher 的属性
this.age = age;
this.major = major;
}
var student = new Student('Tom', 'JS/JQ', 18, 'Computer');
console.log(student);
// 公共原型
function Teacher(){
this.name = 'Tom';
this.tSkill = 'JAVA';
}
Teacher.prototype = {
pSkill: 'JS/JQ'
}
var t = new Teacher();
console.log(t);
function Student(){
this.name = 'Annie'
}
Student.prototype = Teacher.prototype; // 继承Teacher的原型
Student.prototype.age = 18; // 这里更改Student的prototype,Teacher 的 prototype 也会被修改
var s = new Student();
console.log(s);
4. 圣杯模式
解决更改 Student 的 prototype,Teacher 的 prototype 也会被修改的问题:添加一个缓冲构造函数
圣杯模式:圣杯指bufer
function Teacher(){
this.name = 'Tom';
this.tSkill = 'JAVA';
}
Teacher.prototype = {
pSkill: 'JS/JQ',
obj: {a: 1}
}
var t = new Teacher();
console.log(t);
function Student(){
this.name = 'Annie'
}
// 缓冲构造函数
function Buffer(){}
Buffer.prototype = Teacher.prototype;
var buffer = new Buffer();
console.log(buffer);
Student.prototype = buffer; // 相当于继承 Buffer 的原型,继承过来的东西不能修改
Student.prototype.obj.a = 4; // 问题:修改引用值可以修改成功,为什么❓
Student.prototype.age = 18; // 这时候修改Student的prototype 和Teacher的prototype没有关系了
var s = new Student();
console.log(s);
5. CSS 圣杯模式 双飞翼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 清除浮动 */
.clearfix::after{
content: '';
display: table;
clear: both;
}
.wrap {
width: 700px;
margin: 0 auto;
border: 1px solid #000;
}
.top,
.foot {
height: 50px;
background-color: #000;
}
.main {
padding: 0 100px;
overflow: hidden;
}
.main .left,
.main .content,
.main .right {
float: left;
position: relative;
background-color: green;
/* 圣杯模式主要逻辑是赋值 */
margin-bottom: -2000px;
padding-bottom: 2000px;
}
.main .left {
left: -100px;
width: 100px;
}
.main .content {
width: 100%;
margin-left: -100px;
background-color: red;
}
.main .right {
left: 100px;
width: 100px;
margin-left: -100px;
}
</style>
</head>
<body>
<div class="wrap">
<div class="top"></div>
<div class="main clearfix">
<div class="left">123</div>
<div class="content">234<br />234</div>
<div class="right">123</div>
</div>
<div class="foot"></div>
</div>
<script type="text/javascript"></script>
</body>
</html>
6. 闭包
普通闭包
function test(){
var num = 0; // 私有变量
function add(){
num++;
console.log(num);
}
return add;
}
var add = test();
add();
add();
add();
通过对象返回闭包
function test(){
var num = 0; // 私有变量
var compute = {
add: function(){
num++;
console.log(num);
}
}
return compute;
}
var compute = test();
compute.add();
compute.add();
compute.add();
构造函数形成闭包
function Compute(){
var num = 0;
this.add = function(){
num++;
console.log(num);
}
// 这里隐式的 return this 了
}
var compute = new Compute();
compute.add();
7. 以闭包的方式包装圣杯模式
如何将圣杯模式像闭包的方式包装一下 ⭐
// 企业级写法 模块化开发
var inherit = (function test(){ ⭐
var Buffer = function(){}; // 有自己的命名空间了
return function(Target, Origin){
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer(); // new 一定要放在 prototype 赋值的下方
Target.prototype.constructor = Target; // 还原构造器
Target.prototype.super_class = Origin; // 继承源
}
})();
inherit(Student, Teacher);
Teacher.prototype.name = 'Tom';
function Teacher(){}
function Student(){}
function Buffer(){}
Student.prototype.age = 18
var s = new Student();
var t = new Teacher();
console.log(s);
console.log(t);
例1
var inherit = (function test(){
var Buffer = function(){}; // 有自己的命名空间了
return function(Target, Origin){
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer(); // new 一定要放在 prototype 赋值的下方
Target.prototype.constructor = Target; // 还原构造器
Target.prototype.super_class = Origin; // 继承源
}
})();
// 写自启动函数,是因为加载的时候就需要启动,把里面的函数return 给变量initProgrammer保存
// 让 initProgrammer 接收,是因为不想里面抛出来的函数立即执行,让它等待着,等需要执行的时候再执行
var initProgrammer = (function(){ // 在这个函数里面造了一个伪全局,避免了全局污染
var Programmer = function(){};
Programmer.prototype = {
name: '程序员',
tool: '计算机',
work: '编写应用程序',
duration: '10个小时',
say: function(){
console.log('我是一名' + this.myName + this.name + ',我的工作是用' + this.tool + this.work + ', 我每天工作' + this.duration + ',我的工作需要用到' + this.lang.toString() + '。');
}
}
function FrontEnd(){}
function BackEnd(){}
inherit(FrontEnd, Programmer);
inherit(BackEnd, Programmer);
FrontEnd.prototype.lang = ['HTML', 'CSS', 'JavaScript'];
FrontEnd.prototype.myName = '前端';
BackEnd.prototype.lang = ['Node', 'Java', 'SQL'];
BackEnd.prototype.myName = '后端';
return {
FrontEnd: FrontEnd,
BackEnd: BackEnd
}
})();
var frontEnd = new initProgrammer.FrontEnd();
var backEnd = new initProgrammer.BackEnd();
frontEnd.say();
backEnd.say();
例2
window.onload = function(){
init();
}
function init(){
initCompute();
initFunctions();
}
// 按需启动功能
var initCompute = (function(){
var a = 1,
b = 2;
function add(){
console.log(a + b);
}
function minus(){
console.log(a - b);
}
function mul(){
console.log(a * b);
}
function div(){
console.log(a / b);
}
return function(){
add();
minus();
mul();
div()
}
})();
var initFunctions = (function(){
})();
// 或者用插件化开发
;(function(){
var Slider = function(opt){}
Slider.prototype = {
}
window.Slider = Slider;
})();
var slider = new Slider({
});
8. 作业
整合下面3个功能(模块化开发:独立的作用域空间,将抛出来的函数保存到GO)
打印100以内的能被 3 或 5 或7 整除的数
打印斐波那切数列的第n位
打印从0到一个数的累加值
window.onload = function(){
init();
}
function init(){
initFb(10);
initDiv(100);
}
var initFb = (function(){
function fb(n) {
if(n <= 0) {
return 0;
}
if(n <= 2){
return 1;
}
return fb(n - 1) + fb(n - 2);
}
})();
var initDiv = (function(){
function div(n) {
for(var i = 0; i <= n; i++){
if(i % 3 === 0 || i % 5 === 0 || i % 7 === 0) {
arr.push(i);
}
}
return arr;
}
return div;
})();
var acc = (function(){
return function(){
var num = 0;
function add(){
console.log(++num);
}
return add;
}
})()