javascript 内置了许多 function 函数(){...}javascript
js 执行首先就会执行本身内置的函数定义 (function Function、function Object)html
对象的继承java
大部分面向对象的编程语言,都是经过“类”(class)实现对象的继承。c++
传统上,JavaScript 语言的继承不经过 class,而是经过“原型对象”(prototype)实现,称之为 JavaScript 的原型链继承面试
JavaScript 继承机制的设计思想就是,原型对象 prototype 的全部属性和方法,都能被实例对象共享编程
ES6 引入了 class 语法数组
function Cat(name, color) { this.name = name; this.color = color; this.bar = function () { console.log('喵喵'); }; } var cat1 = new Cat('大毛', '白色'); var cat2 = new Cat('二毛', '黑色'); cat1.meow === cat2.meow // false
cat1
和 cat2
是同一个构造函数的两个实例,它们都具备 bar 方法。因为 bar 方法是生成在每一个实例对象上面,因此两个实例就生成了两次。浏览器
prototype
属性,指向一个对象。
function Animal(name) { this.name = name; } Animal.prototype.color = 'white'; var cat1 = new Animal('大毛'); var cat2 = new Animal('二毛'); cat1.color // 'white' cat2.color // 'white'
读取属性/方法,沿着原型链找安全
设置属性/ 方法,只会查看和影响自身编程语言
全部函数都具备 prototype 显式原型属性,指向一个对象____原型对象
全部对象都是某个构造函数的实例,都拥有 __proto__隐式原型属性
注意:
constructor 构造函数____等于函数自己
__proto__ 隐式原型属性____指向 该对象的构造函数的原型对象 prototype (即 隐式原型属性 指向 上一级对象的原型对象)
即 同一构造函数的 全部实例对象 都有一个 隐式原型 指向同一个原型对象
全部函数都是 function Function(){...} 的实例
/* 面试题 */ var A = function() { } A.prototype.n = 1 var b = new A() A.prototype = { // 改变的 只是一个地址值,而不会改变 真正对象 的存在(b 始终指向那个 原始的 prototype 对象) n: 2, m: 3 } var c = new A() console.log(b.n, b.m, c.n, c.m)
function Constr() {} var x = new Constr(); var y = new x.constructor(); y instanceof Constr; // true
这使得在实例方法中,调用自身的构造函数成为可能
Constr.prototype.createCopy = function () { return new this.constructor(); };
function Foo() {} var f = new Foo(); f.constructor.name // "Foo"
全部 函数 都是 function Function(){} 的实例对象,
包括 Object 的构造函数 的 __proto__ 都指向 Function 的原型对象
console.log(Object instanceof Function); // true console.log(Object instanceof Object); // true console.log(Function instanceof Function); // true console.log(Function instanceof Object); // true
var v = new Vehicle(); v instanceof Vehicle; // true
实例对象 instanceOf 构造函数
v instanceof Vehicle // 等同于 Vehicle.prototype.isPrototypeOf(v)
null
对象。这时,instanceof
判断会失真
var obj = Object.create(null); typeof obj; // "object" Object.create(null) instanceof Object; // false
所以,只要一个对象的原型不是null,instanceof运算符的判断就不会失真。
var x = [1, 2, 3]; var y = {}; x instanceof Array // true y instanceof Object // true
注意,instanceof 运算符只能用于对象,不适用原始类型的值。
function Fubar (foo, bar) { if (this instanceof Fubar) { this._foo = foo; this._bar = bar; } else { return new Fubar(foo, bar); } }
让一个构造函数继承另外一个构造函数,是很是常见的需求。这能够分红两步实现
function Sub(value) { Super.call(this); Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub; this.name = value; Sub.prototype.method = '...'; }
另一种写法是 Sub.prototype
等于一个父类实例,可是子类会具备父类实例的方法。有时,这可能不是咱们须要的,因此不推荐使用这种写法
Sub.prototype = new Super();
ClassB.prototype.print = function() { ClassA.prototype.print.call(this); // some code }
这就等于继承了父类A的 print 方法
/**** 旨在实现封装的前提下,最少占用内存 ****/ // 封装 父类 function Parent(name, age){ this.name = name; this.age = age; }; Parent.prototype.setName = function(name){ this.name = name; }; Parent.prototype.setAge = function(age){ this.age = age; }; // 封装 子类 function Child(name, age){ Parent.call(this, name, age); // 继承父类的属性 this.isCrying = false; }; Child.prototype = new Parent(); // 继承父类的方法 Child.prototype.constructor = Child; // 修正构造器指向
function M1() {
this.hello = 'hello';
}
function M2() {
this.world = 'world';
}
function S() {
M1.call(this);
// 继承 M1
S.prototype = Object.create(M1.prototype);
M2.call(this);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
}
var s = new S();
s.hello // 'hello'
s.world // 'world'
子类 S 同时继承了父类 M1 和 M2 。这种模式又称为 Mixin(混入)
var module1 = new Object({ _count : 0, m1 : function (){ //... }, m2 : function (){ //... } });
function StringBuilder() { var buffer = []; this.add = function (str) { buffer.push(str); }; this.toString = function () { return buffer.join(''); }; }
function StringBuilder() { this._buffer = []; StringBuilder.prototype = { constructor: StringBuilder, add: function (str) { this._buffer.push(str); }, toString: function () { return this._buffer.join(''); } }; }
以上代码将私有变量放入实例对象中,好处是看上去更天然,可是它的私有变量能够从外部读写,不是很安全
var module1 = (function () { var age = 0; var getAge = function () { return this.age; }; var setAge = function (age) { this.age = age; }; return { getAge : getAge, setAge : setAge }; })();
使用上面的写法,外部代码没法直接获取内部的_count
变量。
var module1 = (function (mod){ mod.m3 = function () { //... }; return mod; })(module1);
上面的代码为 module1 模块添加了一个新方法 m3(),而后返回新的 module1 模块。
var module1 = (function (mod) { //... return mod; })(window.module1 || {});
与"放大模式"相比,“宽放大模式”就是“当即执行函数”的参数能够是空对象
var module1 = (function ($, YAHOO) { //... })(jQuery, YAHOO);
上面的module1
模块须要使用 jQuery 库和 YUI 库,就把这两个库(实际上是两个模块)看成参数输入module1
。
这样作除了保证模块的独立性,还使得模块之间的依赖关系变得明显
(function($, window, document) { function go(num) { } function handleEvents() { } function initialize() { } function dieCarouselDie() { } //attach to the global scope window.finalCarousel = { init : initialize, destroy : dieCouraselDie } })( jQuery, window, document );
上面代码中,finalCarousel 对象输出到全局,对外暴露 init() 和 destroy() 接口,
内部方法 go()、handleEvents()、initialize()、dieCarouselDie() 都是外部没法调用的。