【前端学习】JavaScript基础

数据类型篇

1. null是一个表示”空”的对象,转为数值时为0;undefined是一个表示“此处无定义”的原始值,转为数值时为NaN

2. JavaScript转换布尔值的规则是:除了undefined、null、false、0、NaN、空字符串转为false以外,其他值都视为true。空数组和空对象对应的布尔值都是true

3. JavaScript的Number精度最多只能到53个二进制位,它对15位的十进制数都可以精确处理

4. JavaScript能够表示的数值范围为21024到2-1023(开区间),超出这个范围的数无法表示

5. NaN不等于任何值,包括它本身。数组的indexOf方法内部使用的是严格相等运算符,所以该方法对NaN不成立

6. 判断NaN更可靠的方法是:利用NaN为唯一不等于自身的值的这个特点,进行判断

7. 对于码点在U+10000到U+10FFFF之间的字符,JavaScript总是认为它们是两个字符(length属性为2)。所以处理的时候,必须把这一点考虑在内。也就是说,JavaScript返回的字符串长度可能是不正确的

8. 为了避免歧义,JavaScript引擎在无法确定一行代码是对象还是代码块时,一律解释为代码块。如果要解释为对象,最好在大括号前加上圆括号

9. 只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。它只能删除对象本身的属性,无法删除继承的属性。即使delete返回true,该属性依然可能读取到值

10. with语句一个很大的弊病是绑定对象不明确,建议不要使用,可以考虑用一个临时变量代替with

11. 采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效

12. 函数的作用域与变量一样,就是其声明时所在的作用域,与运行时所在的作用域无关

13. 函数的length属性与实际传入的参数个数无关,只反映函数预期传入的参数个数。但是,没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined

14. 函数参数如果是原始类型的值,传递方式是传值传递。但是,如果函数参数是复合类型的值,传递方式是传址传递

15. 如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这是将不会影响到原始值

16. 如果有同名的参数,则取最后出现的那个值

17. arguments对象只有在函数体内部才可以使用,且可以在运行时修改。通过arguments对象的length属性,可以判断函数调用时到底带几个参数

18. 立即调用的函数表达式:(function(){ /* code */ }());

19. 数组的数字键不需要连续,length属性的值总是比最大的那个整数键大1

20. 使用delete命令删除一个数组成员,会形成空位,并且不会影响length属性

21. 如果是空位,使用数组的forEach方法、for...in结构以及Object.keys方法进行遍历,空位都会被跳过。如果某个位置是undefined,遍历的时候就不会被跳过

运算符

22. 加法运算符存在重载,可能执行两种运算,使用的时候必须很小心

23. 可以自定义对象的valueOf方法或toString方法,得到想要的结果

24. 如果运算子是一个Date对象的实例,那么会优先执行toString方法

25. 为了得到负数的正确余数值,可以先使用绝对值函数

26. 对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值

27. 如果对一个值连续做两次取反运算,等于将其转为对应的布尔值,与Boolean函数的作用相同,这是一种常用的类型转换的写法

28. 且运算符可以多个连用,这时返回第一个布尔值为false的表达式的值。如果所有表达式的布尔值都为true,则返回最后一个表达式的值

29. 或运算符可以多个连用,这时返回第一个布尔值为true的表达式的值。如果所有表达式都为false,则返回最后一个表达式的值

30. 对取反运算,可以简单记忆成:一个数与自身的取反值相加,等于-1

31. 对一个小数连续进行两次二进制否运算,能达到取整效果,使用二进制否运算取整,是所有取整方法中最快的一种

32. 使用异或运算可以在不引入临时变量的前提下,互换两个变量的值,使用方法:a ^= b, b ^= a, a ^= b

33. 左移即将一个数乘以2的指定次方。向左移动的时候,最高位的符号是一起移动的。它用于二进制数值非常方便,下面是一个将颜色的RGB值转为HEX值的例子:

var color = {r: 186, g: 218, b: 85};

// RGB to HEX
// (1 << 24)的作用为保证结果是6位数
var rgb2hex = function (r, g, b) {
    return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
        .toString(16)    // 先转成十六进制,然后返回字符串
        .substr(1);    // 去除字符串的最高位,返回后面六个字符串
}

rgb2hex(color.r, color.g, color.b)
// "#bada55"

34. 右移运算符基本上相当于除以2的指定次方(最高位即符号位参与移动)

35. 查看一个负整数在计算机内部的储存形式,最快的办法是采用头部补零的右移运算符,如-1 >>> 0会得到4294967295

36. void运算符的作用是执行一个表达式,然后不返回任何值,或者说返回undefined。这个运算符的主要用途是浏览器的书签工具,以及在超级链接中插入代码防止网页跳转。一个实际的例子如下:

<a href="javascript: void(document.form.submit())">
    提交
</a>

37. 赋值运算符(=)和三元条件运算符(?:)是右结合的。除此之外,指数运算符(**)也是右结合的

语法专题

38. 使用Boolean()进行布尔值转换时,所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true

39. return语句的执行是排在finally代码之前,只是等finally代码执行完毕后才返回

40. 建议可以用空格区分两种不同的圆括号(表示函数的调用以及表达式组合的),具体规则如下:

1. 表示函数调用时,函数名与左括号之间没有空格

2. 表示函数定义时,函数名与左括号之间没有空格

3. 其他情况时,前面位置的语法元素与左括号之间,都有一个空格

41. 如果不得不使用全局变量,可以考虑用大写字母表示变量名,这样更容易看出这是全局变量,比如UPPER_CASE

42. 建议switch...case结构可以用对象结构代替,如下:

function doAction(action) {
    var actions = {
        'hack': function () {
            return 'hack';
        },
        'slash': function () {
            return 'slash';
        },
        'run': function () {
            return 'run';
        }
    };

    if (typeof actions[action] !== 'function') {
        throw new Error('Invalid action.');
    }

    return actions[action]();
}

43. console.dir()方法用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示

标准库

44. Object对象的原生方法分成两类:Object本身的方法与Object的实例方法。所谓“本身的方法”就是直接定义在Object对象的方法,所谓实例方法就是定义在Object原型对象Object.prototype上的方法。凡是定义在Object.prototype对象上面的属性和方法,将被所有实例对象共享

45. 如果参数是原始类型的值,Object方法将其转为对应的包装对象的实例。如果Object方法的参数是一个对象,它总是返回该对象,即不用转换。利用这一点,可以写一个判断变量是否为对象的函数:

function isObject(value) {
    return value === Object(value);
}

isObject([])    // true
isObject(true)    // false

46. 通过var obj = new Object()的写法生成新对象,与字面量的写法var obj = {}是等价的。或者说,后者只是前者的一种简便写法

47. 通过自定义对象的valueOf方法,可以得到想要的结果,例子如下:

var obj = new Object();
obj.valueOf = function () {
    return 2;
};

1 + obj    // 3

48. 利用Object.prototype.toString,可以写出一个比typeof运算符更准确的类型判断函数:

var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};

49. Object.keys只返回对象自身的可遍历属性的全部属性名,而Obj.getOwnPropertyNames会将它们都返回

50. 一旦定义了取值函数get(或存值函数set),就不能将writable属性设为true,或者同时定义value属性,否则会报错

51. 如果原型对象的某个属性的writable为false,那么子对象将无法自定义这个属性。但是,有一个规避方法,就是通过覆盖属性描述对象,绕过这个限制

52. 可配置性决定了目标属性是否可以被删除

53. 取值函数与存值函数的一个例子如下:

var obj = {
    $n : 5,
    get next() { return this.$n++ },
    set next(n) {
        if (n >= this.$n) this.$n = n;
        else throw new Error('新的值必须大于当前值');
    }
};

obj.next    // 5
obj.next = 10;
obj.next    // 10
obj.next = 5;
// Uncaught Error: 新的值必须大于当前值

54. shift()方法用于删除数组的第一个元素,并返回该元素;unshift()方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度

55. 用于数组合并的concat方法返回的是一个新数组,原数组不变

56. 如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法

57. reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce是从左到右处理,reduceRight则是从右到左,其他完全一样

58. 某些场合,原始类型的值会自动当作包装对象调用,即调用包装对象的属性和方法。这时,JavaScript引擎会自动将原始类型的值转为包装对象实例,并在使用后立刻销毁实例

59. 由于浮点数的原因,小数5的四舍五入是不确定的,使用的时候必须小心

60. 字符串对象是一个类似数组的对象(很像数组,但不是数组)

61. 新建正则表达式有两种方法,但基本上都采用字面量定义正则表达式

62. 正则实例对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串

63. 如果参数对象有自定义的toJSON方法,那么JSON.stringify会使用这个方法的返回值作为参数,而忽略原对象的其他属性

面向对象编程

64. 应该非常小心,避免不使用new命令,直接调用构造函数

65. 使用new命令时,它后面的函数依次执行下面的步骤:

1. 创建一个空对象,作为将要返回的对象实例

2. 将这个空对象的原型,指向构造函数的prototype属性

3. 将这个空对象赋值给函数内部的this关键字

4. 开始执行构造函数内部的代码

66. 如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象

67. 函数内部可以使用new.target属性,如果当前函数是new命令调用,new.target指向当前函数,否则为undefined

68. 想以现有对象作为模板,生成新的实例对象,可以使用Object.create()方法

69. 简单说,this就是属性或方法“当前”所在的对象

70. 下面这几种用法,都会改变this的指向:

var obj = {
    foo: function () {
        console.log(this);
    }
};

obj.foo()    // obj
(obj.foo = obj.foo)()    // window
(false || obj.foo)()    // window
(1, obj.foo)()    // window

// 上面代码中,obj.foo就是一个值
// 这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj

71. 使用一个变量固定this的值,然后内层函数调用这个变量,是非常常见的做法,请务必掌握。下面是这个做法的一个例子:

var o = {
    f1: function() {
        console.log(this);
        var that = this;
        var f2 = function() {
            console.log(that);
        }();
    }
}

o.f1()
// Object
// Object

72. 函数实例的call方法,可以指定函数内部this的指向,然后在所指定的作用域中,调用该函数

73. 利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组

74. bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数

75. 将Function.prototype.call方法绑定到Function.prototype.bind对象,就意味着bind的调用形式也可以被改写:

function f() {
    console.log(this.v);
}

var o = { v: 123 };
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind(f, o)()    // 123

76. 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)

77. prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。它的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的

78. 有了constructor属性,就可以从一个实例对象新建另一个实例

79. 修改原型对象时,一般要同时修改constructor属性的指向,如下所示:

// 坏的写法
C.prototype = {
    method1: function (...) { ... },
    // ...
};

// 好的写法
C.prototype = {
    constructor: C,
    method1: function (...) { ... },
    // ...
};

// 更好的写法
C.prototype.method1 = function (...) { ... };

80. instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例

81. 让一个构造函数继承另一个构造函数,是非常常见的需求。这可以分成两步实现:

// 第一步:在子类构造函数中,调用父类构造函数
function Sub(value) {
    Super.call(this);
    this.prop = value;
}

// 第二步:让子类的原型指向父类的原型,这样子类就可以继承父类原型
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = '...';

// 另一种写法是Sub.prototype等于一个父类实例
Sub.prototype = new Super();
// 这种写法也有继承的效果,但是子类会具有父类实例的方法,不推荐

82. 可通过变通方法实现多重继承的功能。这种模式又称为Mixin(混入):

function M1() {
    this.hello = 'hello';
}

function M2() {
    this.world = 'world';
}

function S() {
    M1.call(this);
    M2.call(this);
}

// 继承M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入M2
Object.assign(S.prototype, M2.prototype);

// 指定构造函数
S.prototype.constructor = S;

var s = new S();
s.hello    // 'hello'
s.world    // 'world'

83. hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法

84. 在严格模式中,函数内部改变参数与arguments的联系被切断了,两者不再存在联动关系

异步操作

85. 我们可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个:

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
    console.log('参数为 ' + arg + ' , 1秒后返回结果');
    setTimeout(function () { callback (arg * 2) }, 1000);
}

function final(value) {
    console.log('完成:', value);
}

function series(item) {
    if(item) {
        async( item, function(result) {
            results.push(result);
            return series(items.shift());
        });
    } else {
        return final(results[results.length - 1]);
    }
}

series(items.shift());

86. 流程控制函数也可以是并行执行,即所有异步任务同时执行,等到全部完成后,才执行final函数:

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
    console.log('参数为 ' + arg + ' , 1秒后返回结果');
    setTimeout(function () { callback (arg * 2); }, 1000);
}

function final(value) {
    console.log('完成:', value);
}

items.forEach(function(item) {
    async(item, function(result) {
        results.push(result);
        if(results.length === items.length) {
            final(results[results.length - 1]);
        }
    })
});

87. 如果回调函数是对象的方法,那么setTimeout使得方法内部的this关键字指向全局环境,而不是定义时所在的那个对象

88. setTimeout(f, 0)的一大应用是可以调整事件的发生顺序。计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)里面执行

89. Promise对象的报错具有传递性

90. then()有四种用法,它们的差别如下:、

// 写法一
f1().then(function () {
    return f2();
}).then(f3);
// f3回调函数的参数是f2函数的运行结果

// 写法二
f1().then(function () {
    f2();
}).then(f3);
// f3回调函数的参数是undefined

// 写法三
f1().then(f2()).then(f3);
// f3回调参数的参数是f2函数返回的函数运行结果

// 写法四
f1().then(f2).then(f3);
// f2会接收到f1()返回的结果

浏览器模型

91. AJAX只能向同源网址(协议、域名、端口都相同)发出HTTP请求,如果发出跨域请求,就会报错

92. 如果需要跨域AJAX请求发送Cookie,需要withCredentials属性设为true

93. 浏览器安全的基石是“同源政策”(same-origin policy)

94. CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing),它允许浏览器向跨域的服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制

95. ArrayBuffer对象表示一段二进制数据,用来模拟内存里面的数据。通过这个对象,JavaScript可以读写二进制数据。这个对象可以看作内存数据的表达

96. 用户上传文件时,表单<form>元素的method需要设置为POST,enctype属性设为multipart/form-data

posted @ 2020-05-14 18:00  アカツキ  阅读(123)  评论(0编辑  收藏  举报