Skip to content

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 继承 也就是原型链 是可以一级一级往下传的~

image-20230222163933289

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

  1. 实例Event构造函数来创造事件:

javascript var evt = new Event(eventName); dom.dispatchEvent(evt);

  1. 但是问题在于这样触发的事件默认是不冒泡的,需要手动设置冒泡,那么此时应实例化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也刚好可以拿来传额外信息了~

  1. 另外,IE暂不支持这种方法,还应用老式方法:

javascript var evt = document.createEvent('Event'); evt.initEvent(eventName, true, false); //参数分别代表(type, bubbles, cancelable) el.dispatchEvent(evt);

  1. 所以综上,自己做了一个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下中文,其实还是不行,排不了序…