JavaScript 权威指南
Chap. 3 类型、值和变量
不可变的原始值和可变的对象引用:对象的比较均是引用的比较,当且仅当它们引用自同一个对象时才相等。∴ 如果想得到一个对象的副本,必须在循环里显示的复制每一个元素。
Chap. 6 对象
6.1.3 通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性(一般是对象 所以又称原型对象)的值,所以通过new Object()创建的对象也继承自Object.prototype. 换言之,只有构造函数才有prototype属性,实例化对象是没有的。
6.1.4 Object.create()
var o3 = Object.create(Object.prototype); //o3==={}===new Object();
// ∴可以理解成
var a = String.prototype;
var b = Object.create(a); //与var b = new String()效果一样
b instanceof String //true
例:通过原型继承创建一个新对象
function inherit(p){
if(Object.create()) return Object.create(p); //如果有方法 直接调用Object.create()并退出
//以下就是模拟了一个Object.create()
var type = typeof p;
if(type !== 'object' && type !== 'function') throw TypeError(); //判断p符合做原型(对象)的条件不
function F(){}; //定义一个新的构造函数
F.prototype = p; //将其原型属性设定为p
return new F(); //实例化
}
6.2.2 继承 也就是原型链 是可以一级一级往下传的~
6.4 检测属性
- 'x' in obj //x无论是自有属性还是继承属性都会返回true
- obj.hasOwnProperty('x') //自有属性才会返回true
- o.x !== undefined //方法①的简易版 但不能区分值为undefined的属性
6.5 枚举属性
- Object.keys():可枚举的自有属性
- Object.getOwnPropertyNames():所有自有属性
- for/in:所有可枚举属性
function B(){this.b=1}
function C(){this.c=2}
C.protptype = new B();
var cc = new C();
Object.keys(cc) //["c"]
Object.getOwnPropertyNames //["cc"]
for(prop in cc) console.log(prop) //c b
6.6 存取器属性 getter 和 setter
简单的例子:
var a = {
x:1,
y:2,
get hh(){return this.x},
set hh(n){this.x = n}}
a.hh //1
a.hh = 4
a.hh //4
//注意这里this关键字的用法。JS把这些函数当做对象的方法来调用,所以this指向这个点的对象
6.7 设置对象的值、可写性、可枚举性和可配置性
(例6-3:为Object添加extend属性):
Object.defineProperty(Object.prototype, 'extend', {
writable: true,
enumerable: true,
configurable: false,
value: function(that) {
var prop = Object.getOwnPropertyNames(that);
for (var i = 0; i < prop.length; i++) {
if (prop[i] in this) continue;
var des = Object.getOwnPropertyDescriptor(that, prop[i]);
Object.defineProperty(this, prop[i], des);
}
}
})
var a = {x;1, y:2}
var b = {x:3}
b.extend(a) //{x:3, y:2}
6.8
查询对象的原型属性(即构造函数/类.prototype属性):
Object.getPrototypeof(a)
a.constructor.prototype() //为什么a.constructor是function Object(){} a.constructor.prototype是object{}?
检测一个对象是否是另一对象的原型: 原型(属性)/类(函数).prototype**.isPrototypeOf**(对象)
检测一个对象是否是一个函数的实例:对象 **instanceof** 类(函数)
自己写的小测试:
var a ={x:1, y:2}
var b = Object.create(a);
a.isPrototypeOf(b) //true
Object.getPrototypeOf(b) //{x:1, y:2}
b.constructor //function Object(){}
b.constructor.prototype //Object{}
var C = function(){}
C.prototype = a;
var c = new C();
a.isPrototypeOf(c) //true
Object.getPrototypeOf(c) //{x:1, y:2}
c.constructor //function Object(){} 本来理想中的状态
c.constructor.prototype //Object{}
6.8.2 类属性:Object.prototype.toString.call(o).slice(8,-1)
这是为了避免很多对象继承的toString()方法被重写了
插入:关于__proto__
Object.prototypeOf(a) 就相当于a.proto,意味着直接跳过了构造函数而指向了构造函数的prototype属性,亦即得到了该对象的constructor.prototype(知乎也有说__proto__=constructor.prototype 前者是后者的简写)。作用是构成原型链,当我们访问obj这个对象的x属性时,如果在obj中找不到,就会沿着__proto__依次寻找。
内建对象:Array(),Array.prototype.proto__指向什么?Array.prototype也是一个对象,对象就是由Object()创建的,因此Array.prototype.__proto === Object.prototype。Function(), Object()也是一样的道理。最后,Object.prototype.__proto__指向null。
自定义对象 错误记录:
Foo.prototype = {a:10, b:-10}
Foo.prototype //Object{a:10, b:-10};
Foo.prototype.constructor //function Object(){}
记得书里9.2.2讲的!重写prototype时会很容易忘记添加constructor属性的!这时需要手动添加,显示设置构造函数反向引// 用:construtour: Foo;
这里又有一个迷之奇怪的例子…
function Foo(){}
function Bar(){}
function Loo(){}
Bar.prototype = new Loo()
Foo.prototype = new Bar()
var foo = new Foo()
foo.__proto__ //为什么直接跳到了Loo{}?Bar{}呢。。其实代表的是Bar{}的意思!是名字会显示类名而已。比如如果将上式改成function Bar(){ this.y = 2 },那么foo.__proto__就会是Loo{ y:2 }
foo.constructor //为什么是function Loo(){}。。!啊 终于知道是为什么了!!因为这里的.prototype直接被替换成了Loo的prototype呀,书上都有强调,应该显性的再设置一次constructor属性反向引用,比如var foo = new Foo(); Foo.prototype.constructor = Foo; 这样才行呢~
然后我终于撸出了一个正确的原型链例子!!- -
var o = {x:1};
function F1(){};
F1.prototype = o;
var q = new F1();
q.y = 2;
function F2(){};
F2.prototype = q;
var r = new F2();
r.z = 3;
r instanceof F1 //true;
r.constructor // (奇怪的是居然是function Object(){} 唉 但好歹还是个函数,说不定就是指的F2!难道…是①o???!!) 同上,最后要显性设置F2.prototype.constructor = F2;
r.__proto__ //Object{y: 2}
r.__proto__.__proto__ // Object{x: 1}
// 注意!如果不给q加q.y=2的话,那r.__proto__就是没有内容的Object{} 不要误会啦!
Chap. 7 数组
7.9.5 (联合8.8.1) 自制标准差
公式:s=sqrt(((x1-x)^2 +(x2-x)^2 +......(xn-x)^2)/n)
var a = [1, 2, 3, 4, 5];
function sum(x,y) {
return x+y;
}
function square(x){
return x*x;
}
var mean = a.reduce(sum)/a.length;
var devi = a.map(function(x){
return x-mean;
});
var standard = (devi.map(square).reduce(sum))/a.length;
var stddev = Math.sqrt(standard);
Chap. 8 函数
8.3.1 可选形参
function(a, /*optional*/b){
b = b(有传入实参) || 5(无传入实参并设置默认值为5);
//在该例子中b作为形参传入 相当于var b
}
8.4 自定义函数属性
用途:不用定义全局变量,而将信息保存到函数对象的一个属性中,比如例子
a.counter = 0; //由于函数声明被提前了 所以这里可以在函数声明之前给其成员赋值
function a(){
return a.counter++; //先返回H计数器的值,然后计数器自增1
}
8.5 作为命名空间的函数:∴ 插件常常会使用IIFE 立即执行的函数表达式
8.6 闭包-名字由来:是指函数变量可以被隐藏于作用域链之内,因此看起来是函数将变量包裹了起来
插入内容!如何判断一个对象的type:
var a0 = [1,2,3]
Array.①isArray(a0) => true
a0 instanceod Array => true
typeof a0 => 'object'
a0.constructor === Array => true
var b0 = 'hah'
typeof b0 => 'string'
②b0 instanceof String => false
b0.constructor === String => true
var b1 = new String('haha')
typeof b1 => 'object'
b1 instanceof String => true
b1.constructor === String => true
var c0 = 4
typeof c0 => 'number'
c0 instanceof Number => false
c0.constructor === Number =>true
var d0 = new Date();
d0 instanceof Date => true
d0.constructor === Date => true
① 7.10 在ECMAScript3中可以这样模拟:
var isArray = Function.isArray || function(o){
return typeof o === 'object' &&
Object.prototype.toString.call(o) === '[Object Array]';
}
② 3.0 JS的数据类型分为两类:原始类型(数字、字符串、布尔值)和对象类型(五种特殊的类对象Array, Function, Date, RegExp, Error)
③ 3.6 包装对象:存取字符串、数字或布尔值的属性时创建的临时对象
Q: 字符串既然不是对象,为什么会有属性?
A: 这是因为只要引用了字符串s的属性,js就会将字符串通过调用new String(s)的方法转化成临时对象,这个对象继承了字符串的方法。一旦属性引用结束,这个新创建的对象就会被销毁。
如 b0.a = 'good'; b0.a // => undefined; b1.a='good'; b1.a // => 'good';
插入内容:
在函数里不要console.log('this is' + this); 这会调用this对象的toString方法 从而显示出来是[object object]
也不要在this为window里调用console.log(JSON.stringify(this)) 会报“Uncaught TypeError: Converting circular structure to JSON(…)” 这是因为若this为/window,则window里一定会包括自己
Chap. 9 类和模块
9.2.1 有一个对象r, 想知道r是否是Range对象的实例 那么应该:r instanceof Range //instanceof运算符并不会检查 r 是否由 Range()构造函数初始化而来,而是会检查 r 是否继承自 Range.prototype
9.4 类的扩充
String.prototype.trim = String.prototype.trim || function(){
if(!this) return this; //空字符串不作处理
return this.replace(/^\s+|\s+$/g,'');
}
9.6.1 例子:集合类
有给protptype写的方法有add, contains, size, foreach。
注意事项:①给add支持链式方法调用-就是最后return this; ②自制foreach可以传入f和context两个参数,那么在foreach函数里写得for循环里,就可以直接f.call(context, this.values[s])来绑定上下文以及传入循环到的数值了。③本来已经有了一个Set.prototype.add了,那为什么还要在function Set(){…; this.add.apply( this, arguments)}呢?这样就可以new Set(1,2,3,4)了呀!~:D
9.6.2 例子:枚举类型
①工厂方法与构造函数 ②改写默认的toString和valueOf和toJSON方法 ③遍历obj中的每个key:for(var name in obj) { name...}
Chap. 17 事件
17.1.1 window 事件里的 load 事件
load事件:在文档和所有外部资源(比如图片)完全加载后
DOMContentLoaded和readystatechange是load事件的替代方案,只要文档和元素加载完,不管外部元素是否加载完就可以触发了
17.3.6 三个阶段:事件捕获(完)>调用目标元素上注册的处理函数(完) > 冒泡(调用在目标的祖父元素上注册的事件处理程序)
focus, blur和scroll事件不会冒泡
17.5 单次拖动文档元素
<!DOCTYPE html>
<html>
<head>
<title>Drag-example</title>
</head>
<body>
<div style="position: absolute;left: 100px;top: 100px;widith:250px;background: gray;border:solid black">
<div onmousedown="drag(this.parentNode, event)" style="background:gray; border-bottom: dotted black;padding: 3px;"><p>this is a test</p></div>
</div>
</body>
<script type="text/javascript">
function drag(elementToDrag, event){
console.log(event)
var startX = event.clientX + window.pageXOffset;
var startY = event.clientY + window.pageYOffset;
var origX = elementToDrag.offsetLeft + window.pageXOffset;
var origY = elementToDrag.offsetTop + window.pageYOffset;
var deltaX = startX - origX;
var deltaY = startY - origY;
document.addEventListener('mousemove',moveHandler, true);
document.addEventListener('mouseup', upHandler, true);
event.stopPropagation();
event.preventDefault();
function moveHandler(e){
elementToDrag.style.left = e.clientX + window.pageXOffset - deltaX +'px';
elementToDrag.style.top = e.clientY + window.pageYOffset - deltaY + 'px';
console.log(window.pageYOffset)
e.stopPropagation();
}
function upHandler(e){
document.removeEventListener('mouseup',upHandler,true);
document.removeEventListener('mousemove', moveHandler, true);
e.stopPropagation();
}
} //js的模块也被一整个function包了起来~
</script>
</html>
书上的原例有加上更详细的兼容ie9以下版本!
注意:mousemove和mouseup处理程序注册为捕获事件处理程序。
另外:e.altKey, ctrlKey, shiftKey 制定了当事件发生时是否有键盘辅助键…
关于 trigger
- 实例Event构造函数来创造事件:
javascript
var evt = new Event(eventName);
dom.dispatchEvent(evt);
- 但是问题在于这样触发的事件默认是不冒泡的,需要手动设置冒泡,那么此时应实例化CustomEvent构造函数:
javascript
var evt = new CustomEvent(eventName,{bubbles:true,detail:'extra message'});
dom.dispatchEvent(evt);
ps: detail是专门用来传额外信息的property,new CustomEvent时不能够自己随意添加其它属性名。但我们可能记得在firefox里e.detail在滚轮事件里与上下滚动有关,不过不用担心detail属性被覆盖这点。一是在平常情况下并不需要手动触发滚动事件(而是直接更改网页的scrollTop);二是哪怕直接t触发滚动事件网页也不会滚动,因此对应的e.detail并不会是3/-3,而是默认的null,那么e.detail也刚好可以拿来传额外信息了~
- 另外,IE暂不支持这种方法,还应用老式方法:
javascript
var evt = document.createEvent('Event');
evt.initEvent(eventName, true, false); //参数分别代表(type, bubbles, cancelable)
el.dispatchEvent(evt);
- 所以综上,自己做了一个trigger的小包装:
javascript
function trigger(el, event, detail) {
if (typeof CustomEvent === 'function') {
var evt = new CustomEvent(eventName,{bubbles:true,detail:detail})
} else {
var evt = document.createEvent('Event');
evt.initEvent(eventName, true, false);
}
el.dispatchEvent(evt);
}
Error
如果try里有return的话,return的值会在try里就已经固定了。但走完finally的流程才会吐出来。
http://javascript.ruanyifeng.com/grammar/error.html
另 有哪几种Error的种类呢?以下这些Error实际上都是继承自Error的
- EvalError
- RangeError (比如堆栈溢出)
- SyntaxError
- ReferenceError
- TypeError
- URIError
所以如果是js自动报错的话,可以根据e.name来看错误是哪种type
若自己throw new Error(),那这个type就是Error
若自己throw new ReferenceError(),那这个type才是ReferenceError
json
how to 在
里格式化展现json而不是一行?JSON.stringify() 的第三个参数为数字时“指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格”
String
这里有个有趣的议题:order by中文开头
会用到String.localCompare: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
['张三','李四','王五'].sort((a, b) => a.localeCompare(b, 'zh-Hans-CN', {sensitivity: 'accent'}))
我之前有想到的是encode下中文,其实还是不行,排不了序…