继承

mixin混合继承

    function mixin(obj1, obj2) {
for (var key in obj2) {
//重复不复制
if (!(key in obj1)) {
obj1[key] = obj2[key];
}
}
return obj1;
}

  这种复制是浅复制,对象或者数组函数等都是同一个引用,改变obj1的会同时影响obj2。

寄生继承

  ...

隐式继承

  子类调用fn.call(this)

  深拷贝需要重新声明一个变量(对象),遍历(递归)复制,详情见我的函数技巧,不贴出来了。

原型

  Javascript对象中有一个特殊的[[prototype]]内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时[[prototype]]属性都会被赋予一个非空的值。

    var f = {
a: 1
};
// 创建一个对象 原型为f
var f2 = Object.create(f);
// 通过原型链找到了属性a
console.log(f2.a);

  使用for..in遍历对象的原理和原型链类似,任意可枚举在原型链上的属性都会被遍历。使用in操作符检查属性时也会查找对象原型链,无论是否可枚举。

  所有普通对象的原型最终指向Object.prototype。

  详细讲解一个对象赋值语句:

    var obj = {};
obj.a = 1;

  这里有四种情况:

  1、obj中存在a属性,就会被修改。

    var obj = {
a: 2
};
obj.a = 1;
console.log(obj.a); //

  2、obj的原型链不存在a属性,就会被直接添加到obj上。

    var obj = {};
console.log('a' in obj); //false
obj.a = 1;
console.log(obj.a); //

  3、obj与obj的原型链都存在a属性,就会发生屏蔽,obj中的a会屏蔽原型链上的a。

    var obj2 = {
a: 2
};
var obj = Object.create(obj2);
obj.a = 1;
console.log(obj.a); //

  4、obj的原型链上存在a属性,而obj不存在时,会出现多种情况。

  在原型链上存在a属性且没有被标记为只读,那就会直接在obj添加一个a属性。(情况3)

  在原型链上存在a属性且被标记为只读,那么无法创建该属性,严格模式会报错,普通模式赋值语句会被忽略。

    // 在'use strict'模式下
// Cannot assign to read only property 'a' of object '#<Object>'
var obj2 = {};
Object.defineProperty(obj2, 'a', {
value: 2,
configurable: true,
enumerable: true,
writable: false
})
var obj = Object.create(obj2);
obj.a = 1; //无效
console.log(obj.a); //

  如果在原型链上存在a并且它是一个setter,那就一定会调用这个setter。a不会被添加到obj,也不会重新定义setter。

    var obj2 = {
set a(val) {
console.log(1);
}
};
var obj = Object.create(obj2);
obj.a = 1; // 执行set并输出1

  如果希望怎么样都添加属性,请使用Object.defineProperty(...)。

关于prototype

  所有函数默认都会拥有一个名为prototype的公有不可枚举属性,它会指向另外一个对象:

    function fn() {
console.log(1);
}
console.log(fn.prototype); //Object{}

  这个对象通常被称为fn的原型,实际上不如叫fn.prototype。

    function fn() {
console.log(1);
}
var f = new fn();
console.log(f.__proto__ === fn.prototype); //true

  在调用new fn()时,会创建一个对象,并给一个内部[[prototype]]链接,连接到fn.prototype。个人感觉__proto__这个浏览器私有实现的属性叫原型比较好,毕竟原型链是通过这个属性向上查找的。

  实际上,new操作符实际上并没有直接创建关联,这只是一个副作用。

  通过Object.create()方法才是正规创建原型链接的方法。

  上一段代码很容易让人认为fn是一个构造函数,因为这里用new来调用它并构造出一个对象。

  实际上,fn和普通的函数没有区别。函数本身不是构造函数,当在普通的函数前面加上new时,就会把这个函数调用变成了一个‘构造函数调用’。实际上,new会劫持所有普通函数并用构造形式来调用它。

  

  考虑下面一段代码。

    function fn(a) {
this.a = a;
}
fn.prototype.getA = function() {
return this.a;
}
var f1 = new fn(1);
var f2 = new fn(2);
console.log(f1.getA()); //
console.log(f2.getA()); //

  这段代码展示了两种面向类的技巧:

  1、this.name=name给每个对象都绑定了.name属性。

  2、fn.prototype.getA=...给原型添加了一个方法,现在,每一个实例都可以调用getA方法。

  看起来,似乎创建f1、f2时会把对象复制到这两个新对象中,然而实际上只是通过原型链向上查找调用了方法而已。

关于constructor

    function fn1() {};
var f1 = new fn1();
console.log(f1.constructor === fn1); //true
//替换默认原型
function fn2() {};
fn2.prototype = {};
var f = new fn2();
console.log(f.constructor === fn2); //false
console.log(f.constructor === Object); //true

  当前用新对象替换fn原型时,new出来的新对象不会自动获得constructor属性。所以,不能说因为f.constructor===fn属性,就认为fn构造了对象f。

  实际上,new出来的对象f并没有.constructor属性,它会委托原型去查找该属性,默认的原型(fn.prototype)有construtor属性并且指向fn,所以f.constructor(实际上调用的是fn.prototype.constructor)会指向fn。但是如果替换了fn.prototype,新的原型对象并不会默认有.construtor,于是委托会一直提交到Object.prototype,恰好Object.prototype.constructor===Object,结果也在上面代码中展示出来了。

  可以手动给新原型添加constructor属性:

    function fn2() {};
fn2.prototype = {};
fn2.prototype.constructor = fn2; //修正原型链
var f = new fn2();
console.log(f.constructor === fn2); //true
console.log(f.constructor === Object); //false

  看,修复了!(实际上应该用Object.defineProperty来定义constructor,因为该属性应该是不可枚举的)

  所以说,constructor并不是一个不可变属性,它只是默认不可枚举,但是值可以被任意修改。

原型继承

  常见误用形式和正确使用方式:

    function fn1() {};

    function fn2() {};
//不可以 只是复制引用
//fn1.prototype = fn2.prototype;
//可以实现 但是会执行fn2函数 可能出现额外问题
//fn1.prototype=new fn2;
//ES6前 需要抛弃fn1默认的prototype 可能还要修正constructor属性
fn1.prototype = Object.create(fn2.prototype);
//ES6语法 直接修正默认prototype
Object.setPrototypeOf(fn1.prototype, fn2.prototype);

  

  如何找出任意对象的原型链呢?有一个方法是instanceof。

    function fn() {}
var f = new fn;
console.log(f instanceof fn); //true

  instanceof操作符左边是一个对象,右边是一个函数。该操作解决的问题是:在f的原型链上,是否有fn.prototype对象?(通过bind强绑生成的函数没有prototype属性)

  如果要直接判断两个对象是否存在原型关系,可以用以下几个方法:

    function fn() {}
var f = new fn;
//是否是原型关系
console.log(fn.prototype.isPrototypeOf(f)); //true
//展示原型
console.log(Object.getPrototypeOf(f)); //Object{}
//浏览器私有实现
console.log(f.__proto__); //Object{}

  绝大多数浏览器支持__proto__方法来访问[[prototype]]属性。(__开头的属性表明这不是ECMA标准,还有很多其他的属性也以__开头)

  现在ES6可以用Object.getPrototypeOf()与Object.setPropertyOf()来获取和设置原型,相当于原生支持了__proto__。

  

  Object.create()会创建一个对象,并关联到参数对象中,避免了new操作符与生成对应的constructor,prototype。

  如果旧浏览器不支持,可以用下面的代码模拟:

    if (!Object.create) {
Object.create = function(o) {
function f() {};
f.prototype = o;
return new f();
}
}

  关于new操作符和原型,如果下面的代码可以理解,那就没问题了~

    function fn(a) {
this.a = a;
}
fn.prototype = {};
Object.defineProperty(fn.prototype, 'a', {
value: 1,
configurable: true,
enumerable: true,
writable: false
});
//严格模式new会报错
var f = new fn(3);
console.log(f); //无效!
console.log(f.a); //

  完结撒花!

读书笔记-你不知道的JS上-混入与原型的更多相关文章

  1. 读书笔记-你不知道的JS上-对象

    好想要对象··· 函数的调用位置不同会造成this绑定对象不同.但是对象到底是什么,为什么要绑定他们呢?(可以可以,我也不太懂) 语法 对象声明有两个形式: 1.字面量 => var obj = ...

  2. 读书笔记-你不知道的JS上-this

    关于this 与静态词法作用域不用,this的指向动态绑定,在函数执行期间才能确定.感觉有点像C++的多态? var a = 1; var obj = { a: 2, fn: function() { ...

  3. 读书笔记-你不知道的JS上-函数作用域与块作用域

    函数作用域 Javascript具有基于函数的作用域,每声明一个函数,都会产生一个对应的作用域. //全局作用域包含f1 function f1(a) { var b = 1; //f1作用域包含a, ...

  4. 读书笔记-你不知道的JS上-词法作用域

    JS引擎 编译与执行 Javascript引擎会在词法分析和代码生成阶段对运行性能进行优化,包含对冗余元素进行优化(例如对语句在不影响结果的情况下进行重新组合). 对于Javascript来说,大部分 ...

  5. 读书笔记-你不知道的JS上-闭包与模块

    闭包定义 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行. 看一段最简单的闭包代码: function foo() { var a = 2; //闭包 fun ...

  6. 读书笔记-你不知道的JS上-声明提升

    变量声明提升 Javascript代码一般情况下是由上往下执行的,但是有些情况下不成立. a = 2; //变量声明被提升在当前作用域顶部 var a; console.log(a); console ...

  7. 读书笔记-你不知道的JavaScript(上)

    本文首发在我的个人博客:http://muyunyun.cn/ <你不知道的JavaScript>系列丛书给出了很多颠覆以往对JavaScript认知的点, 读完上卷,受益匪浅,于是对其精 ...

  8. 读书笔记-你不知道的JS中-promise

    之前的笔记没保存没掉了,好气,重新写! 填坑-- 现在与将来 在单个JS文件中,程序由许多块组成,这些块有的现在执行,有的将来执行,最常见的块单位是函数. 程序中'将来'执行的部分并不一定在'现在'运 ...

  9. 读书笔记-你不知道的JS中-promise(2)

    继续填坑 模式 考虑下面的代码: function fn(x) { //do something return new Promise(function(resolve, reject) { //调用 ...

随机推荐

  1. Shiro初识与总结

    1.1简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序 ...

  2. SQL映射文件实现多种方式查询

    1.单条件查询在test中代码如下 2.多条件查询时需要把查询条件编辑为对象或者是集合传入,例如 通过对象进行查询 或者是通过集合进行查询列如Map集合 还有就是通过@Param注解实现多参数的入参, ...

  3. ArrayList,LinkedListd等容器使用时注意点:

    1.对这两个List(包括其他的类似容器),如果向里面加入一个元素(引用数据类型),那么这个List里面保存的是这个对象的引用: 如果想要避免这种现象可以这样:在加入新的元素时不直接压,将已有的对象复 ...

  4. 再说AutoComplete

    一.简述 昨天support一同事,帮她的客户做类似下面的效果(自动完成): 以前在搜房的时候,弄过这个,调用楼盘字典: 这是一个小功能,也是一个大功能.因为它可以做大,也可以做小. 二.搜房的Aut ...

  5. JS--微信浏览器复制到剪贴板实现

    由于太忙很久没写博客了,如有错误遗漏,请指出,感谢! 首先这里要注意,是微信浏览器下的解决方案,其他浏览器请自行测试. 先说复制到剪贴板主要有什么使用场景: 优惠券优惠码,需要用户复制 淘宝商品,需要 ...

  6. java GUI编程一

    一.AWT介绍 所有的可以显示出来的图形元素都称为Component,Component代表了所有的可见的图形元素,Component里面有一种比较特殊的图形元素叫Container,Containe ...

  7. linux debian 9 配置postgresSQL数据库

    #读者注意:本文可以选择不看解释,直接执行每段的0中的代码 (〇):一些概念(可以跳过直接使用(一)0的代码) 1. 客户端:psql.postgreSQL的命令行客户端程序,在终端输入psql进入p ...

  8. Day3 Python基础学习——文件操作、函数

    一.文件操作 1.对文件操作流程 打开文件,得到文件句柄并赋值给一个变量 通过文件句柄对文件进行操作 关闭文件 #打开文件,读写文件,关闭文件 http://www.cnblogs.com/linha ...

  9. docker镜像文件导入与导出

    工作中经常需要拉取一些国外的镜像,但是网络限制等原因在公司拉取很慢,所以我习惯用亚马逊服务器拉取镜像,导出后下载到本地再导入开发环境 1. 查看镜像id sudo docker images REPO ...

  10. JAVA提高三:反射总结

    为前期学习过反射,再这里再次复习总结下:[转载请说明来源:http://www.cnblogs.com/pony1223/p/7659210.html ] 一.透彻分析反射的基础_Class类 Cla ...