1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# 原型 // js中没有class的概念,所以需要使用prototype(原型)来模拟 // 把Studuent理解为一个抽象的class,但其实它就是个实例 var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; var xiaoming = { name: '小明' }; // 将小明的原型指向Student,看上去好像小明继承了Student xiaoming.__proto__ = Student; xiaoming.name; // 小明 xiaoming.run(); // 小明 is running... // 实际编写中,不要直接使用__proto__去改变一个对象的原型 function createStudent(name) { // 基于Student原型创建一个新对象: var s = Object.create(Student); // 初始化新对象: s.name = name; return s; } var xiaoming = createStudent('小明'); xiaoming.run(); // 小明 is running... xiaoming.__proto__ === Student; // true // JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。 // 当我们定义obj.xxx访问一个对象属性时,如果没找到,就会去它的原型对象上找 // 如果一直没找到,就会上溯到Object.prototype对象 // 如果还没有,就返回undefined,下面是arr的原型链例子(类似于php中的多重继承) // arr ----> Array.prototype ----> Object.prototype ----> null // 除了用{}创建对象外,还可以使用构造函数创建对象,这种写法更像class的写法了 // Student看上去就是一个普通函数,但是使用new关键字后,就变成了构造函数 // 它绑定的this指向新创建的对象,并默认返回this function Student(name) { this.name = name; this.hello = function () { alert('Hello, ' + this.name + '!'); } } // xiaoming -> Student.prototype -> Object.prototype -> null var xiaoming = new Student('小明'); xiaoming.hello(); // 用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身: xiaoming.constructor === Student.prototype.constructor; // true Student.prototype.constructor === Student; // true Object.getPrototypeOf(xiaoming) === Student.prototype; // true xiaoming instanceof Student; // true // 上面的写法其实还是有一些问题的,如果再创建小红、小张、小李... // 都会创建各自的hello方法,这样太浪费内存了,可以让他们共享一个hello function Student(name) { this.name = name; } Student.prototype.hello = function() { alert('Hello, ' + this.name + '!'); }; // 漏下new在strict模式下会报错,非strict模式下可以会造成意想不到的结果 // 所以规定构造函数首字母应该大写,一般函数首字母应该小写 // 这样使用jslint检查工具可以检测到漏下new关键字 // 一个常用的编程模式像这样,好处是可以不写new,而且参数比较灵活 // 可以不传参数,也可以分开传 function Student(props) { this.name = props.name || '匿名'; // 默认值为'匿名' this.grade = props.grade || 1; // 默认值为1 } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); }; function createStudent(props) { return new Student(props || {}) } var xiaoming = createStudent({ name: '小明' }); xiaoming.grade; // 1 # 原型继承 'use strict'; function Student(props) { this.name = props.name || 'Unnamed'; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); } // PrimaryStudent构造函数 function PrimaryStudent(props) { // 在Student的构造函数中绑定this变量(调用父类构造函数) Student.call(this, props); this.grade = props.grade || 1; } // 调用了父类的构造函数不等于继承了父类,因为原型链没有改变 // new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null // 利用空函数桥接,可以使得原型链变为如下 // new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null function inherits(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; } inherits(PrimaryStudent, Student); // 继续在PrimaryStudent原型(就是new F()对象)上定义方法 PrimaryStudent.prototype.getGrade = function () { return this.grade; }; var xiaoming = new PrimaryStudent({ name: '小明', grade: 2 }); // 验证 alert(xiaoming.__proto__ === PrimaryStudent.prototype); // true alert(xiaoming.__proto__.__proto__ === Student.prototype); // true alert(xiaoming instanceof PrimaryStudent); // true alert(xiaoming instanceof Student); // true # ES6新增了class关键字 // 有了class关键字,可以更方便地定义类和继承了 // 但是浏览器支持可能还不太好 // 可以试试Babel这个工具将class代码转换成prototypte代码 class Student { constructor(name) { this.name = name; } hello() { alert('Hello, ' + this.name + '!'); } } class PrimaryStudent extends Student { constructor(name, grade) { super(name); this.grade = grade; } myGrade() { alert('I am at grade ' + this.grade); } } |