博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
温故知新之JS基础
阅读量:6519 次
发布时间:2019-06-24

本文共 6978 字,大约阅读时间需要 23 分钟。

数据类型

  1. NAN

    NaN === NaN; // false

    唯一能判断NaN的方法是通过isNaN()函数:

    isNaN(NaN); // true
  2. 浮点数的相等比较:

    1 / 3 === (1 - 2 / 3); // false

    这不是JavaScript的设计缺陷。浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:

    Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
  3. 避免使用new Array(1, 2, 3); // 创建了数组[1, 2, 3]方式

字符串

  1. 需要特别注意的是,字符串是不可变的

    如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果:

    var s = 'Test';s[0] = 'X';alert(s); // s仍然为'Test'
  2. JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串

数组

  1. 大多数其他编程语言不允许直接改变数组的大小,越界访问索引会报错。然而,JavaScript的Array却不会有任何错误。在编写代码时,不建议直接修改Array的大小,访问索引时要确保索引不会越界。

  2. 对原数组进行操作的方法和返回新数组的方法

    原数组:

    • pop/push,unshift/shift

    • sort

    • reverse

    • splice

    新数组:

    • slice

    • concat

    join返回的是新的字符串

  3. 扩展:数组扁平化的几种方法

    var myArray = [[1, 2],[3, 4, 5], [6, 7, 8, 9]];
    • 使用concat()和apply()

      var myNewArray = [].concat.apply([], myArray);// [1, 2, 3, 4, 5, 6, 7, 8, 9]
    • 使用reduce()

      var myNewArray = myArray.reduce(function(prev, curr) {  return prev.concat(curr);});// [1, 2, 3, 4, 5, 6, 7, 8, 9]

    • 使用 ES6 的展开运算符

      var myNewArray4 = [].concat(...myArray);console.log(myNewArray4);// [1, 2, 3, 4, 5, 6, 7, 8, 9]

    • 最后这个方法返回的是字符串

      myArray.join(',')//"1,2,3,4,5,6,7,8,9"
  4. 请注意,for ... in对Array的循环得到的是String而不是Number

对象

  1. 注意,最后一个键值对不需要在末尾加,,如果加了,有的浏览器(如低版本的IE)将报错。

  2. 访问属性是通过.操作符完成的,但这要求属性名必须是一个有效的变量名

    var xiaohong = {    name: '小红',    'middle-school': 'No.1 Middle School'};

    xiaohong的属性名middle-school不是一个有效的变量,就需要用''括起来。访问这个属性也无法使用.操作符,必须用['xxx']来访问:

    xiaohong['middle-school']; // 'No.1 Middle School'xiaohong['name']; // '小红'xiaohong.name; // '小红'
  3. 访问不存在的属性不报错,而是返回undefined

函数

  1. 小心你的return语句

    如果把return语句拆成两行:

    function foo() {    return        { name: 'foo' };}foo(); // undefined

    要小心了,由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:

    function foo() {    return; // 自动添加了分号,相当于return undefined;        { name: 'foo' }; // 这行语句已经没法执行到了}

    所以正确的多行写法是:

    function foo() {    return { // 这里不会自动加分号,因为{表示语句尚未结束        name: 'foo'    };}
  2. 变量提升

    由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。

  3. 闭包应用:封装私有变量

    function create_counter(initial) {    var x = initial || 0;    return {        inc: function () {            x += 1;            return x;        }    }}
  4. 箭头函数

    箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种只包含一个表达式,连{ ... }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }和return
    返回对象:

    // SyntaxError:x => { foo: x }// ok:x => ({ foo: x })

    箭头函数完全修复了this的指向,箭头函数内部的this是词法作用域,由上下文确定

  5. 扩展:装饰器

    现在假定我们想统计一下代码一共调用了多少次parseInt(),可以把所有的调用都找出来,然后手动加上count += 1,不过这样做太傻了。最佳方案是用我们自己的函数替换掉默认的parseInt():

    var count = 0;var oldParseInt = parseInt; // 保存原函数window.parseInt = function () {    count += 1;    return oldParseInt.apply(null, arguments); // 调用原函数};// 测试:parseInt('10');parseInt('20');parseInt('30');count; // 3

对象

  1. 需要遵守的规则

    • 不要使用new Number()、new Boolean()、new String()创建包装对象;

    • parseInt()parseFloat()来转换任意类型到number;

    • String()来转换任意类型到string,或者直接调用某个对象的toString()方法;

    • 通常不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {...};

    • typeof操作符可以判断出number、boolean、string、function和undefined;

    • 判断Array要使用Array.isArray(arr)

    • 判断null请使用myVar === null

    • 判断某个全局变量是否存在用typeof window.myVar === 'undefined'

    • 函数内部判断某个变量是否存在用typeof myVar === 'undefined'

  2. 获取时间戳

    if (Date.now) {    alert(Date.now()); // 老版本IE没有now()方法} else {    alert(new Date().getTime());}
  3. JSON.stringify()

    • 格式化

      var xiaoming = {    name: '小明',    age: 14,    gender: true,    height: 1.65,    grade: null,    'middle-school': '\"W3C\" Middle School',    skills: ['JavaScript', 'Java', 'Python', 'Lisp']};undefinedJSON.stringify(xiaoming, null, '  ');"{  "name": "小明",  "age": 14,  "gender": true,  "height": 1.65,  "grade": null,  "middle-school": "\"W3C\" Middle School",  "skills": [    "JavaScript",    "Java",    "Python",    "Lisp"  ]}"
    • 筛选数据

      第二个参数用于控制如何筛选对象的键值,如果我们只想输出指定的属性,可以传入Array:

      JSON.stringify(xiaoming, ['name', 'height'], '  ');
      "{  "name": "小明",  "height": 1.65}"

      也可以传入函数进行处理

浏览器

  1. 获取UA

    function whatBrowser() {      document.Browser.Name.value=navigator.appName;      document.Browser.Version.value=navigator.appVersion;      document.Browser.Code.value=navigator.appCodeName;      document.Browser.Agent.value=navigator.userAgent;  }

    请注意,navigator的信息可以很容易地被用户修改,所以JavaScript读取的值不一定是正确的

  2. cookie安全问题

    由于JavaScript能读取到页面的Cookie,而用户的登录信息通常也存在Cookie中,这就造成了巨大的安全 隐患。为了解决这个问题,服务器在设置Cookie时可以使用httpOnly,设定了httpOnly的Cookie将不能被JavaScript读取。这个行为由浏览器实现,主流浏览器均支持httpOnly选项,IE从IE6 SP1开始支持。
    为了确保安全,服务器端在设置Cookie时,应该始终坚持使用httpOnly。

  3. 按字符串顺序重新排序DOM节点

    1. Scheme
    2. JavaScript
    3. Python
    4. Ruby
    5. Haskell
    var ol = document.getElementById('test-list'),    lis = [].slice.call(ol.children);lis.sort((a,b)=>  a.innerText.toUpperCase() > b.innerText.toUpperCase());lis.forEach(x=>{ol.appendChild(x)})
  4. children属性时刻都在变化

    当你遍历一个父节点的子节点并进行删除操作时,要注意,children属性是一个只读属性,并且它在子节点变化时会实时更新。
    例如,对于如下HTML结构:

    First

    Second

    当我们用如下代码删除子节点时:

    var parent = document.getElementById('parent');parent.removeChild(parent.children[0]);parent.removeChild(parent.children[1]); // <-- 浏览器报错

    浏览器报错:parent.children[1]不是一个有效的节点。原因就在于,当<p>First</p>节点被删除后,parent.children的节点数量已经从2变为了1,索引[1]已经不存在了。

  5. 扩展练习

    把与Web开发技术不相关的节点删掉

    • JavaScript
    • Swift
    • HTML
    • ANSI C
    • CSS
    • DirectX
    var parent = document.getElementById('test-list');var children = [].slice.call(parent.children);  //Array.prototype.slice.call()children.forEach((element) => {    for(var s of ['Swift', 'ANSI C', 'DirectX']){        if(element.innerText == s){            parent.removeChild(element);        }    }});

事件

  1. 需要特别注意的是,下面这种写法是无效的:

    // 绑定事件:a.click(function () {    alert('hello!');});// 解除绑定:a.off('click', function () {    alert('hello!');});

    这是因为两个匿名函数虽然长得一模一样,但是它们是两个不同的函数对象,off('click', function () {...})无法移除已绑定的第一个匿名函数。

    为了实现移除效果,可以使用off('click')一次性移除已绑定的click事件的所有处理函数。
    同理,无参数调用off()一次性移除已绑定的所有类型的事件处理函数。

  2. 事件触发条件

    一个需要注意的问题是,事件的触发总是由用户操作引发的,但是,如果用JavaScript代码去改动,将不会触发change事件
    有些时候,我们希望用代码触发change事件,可以直接调用无参数的change()方法来触发该事件

    var input = $('#test-input');input.val('change it!');input.change(); // 触发change事件

    input.change()相当于input.trigger('change'),它是trigger()方法的简写。

  3. 浏览器安全限制

    在浏览器中,有些JavaScript代码只有在用户触发下才能执行,例如,window.open()函数:

    // 无法弹出新窗口,将被浏览器屏蔽:$(function () {    window.open('/');});

    这些“敏感代码”只能由用户操作来触发:

    var button1 = $('#testPopupButton1');var button2 = $('#testPopupButton2');function popupTestWindow() {    window.open('/');}button1.click(function () {    popupTestWindow();});button2.click(function () {    // 不立刻执行popupTestWindow(),100毫秒后执行:    setTimeout(popupTestWindow, 100);});

    当用户点击button1时,click事件被触发,由于popupTestWindow()在click事件处理函数内执行,这是浏览器允许的,而button2的click事件并未立刻执行popupTestWindow(),延迟执行的popupTestWindow()将被浏览器拦截。

Ajax

  1. open方法的第三个参数

    千万不要把第三个参数指定为false,否则浏览器将停止响应,直到AJAX请求完成。

  2. 跨域方案

    • flash插件,现在用的很少

    • 代理服务器,需要服务器端做额外开发

    • jsonp,只支持get请求

    • cors,通过http头的Access-Control-Allow-Origin验证

编写jquery插件原则

  • $.fn绑定函数,实现插件的代码逻辑;

  • 插件函数最后要return this;以支持链式调用;

  • 插件函数要有默认值,绑定在$.fn.<pluginName>.defaults上;

  • 用户在调用时可传入设定值以便覆盖默认值。

扩展jQuery对象的功能十分简单,但是我们要遵循jQuery的原则,编写的扩展方法能支持链式调用、具备默认值和过滤特定元素,使得扩展方法看上去和jQuery本身的方法没有什么区别。

此篇跳过了js中的面向对象,高阶函数,正则,原型等难点,下篇等我好好研究一下继续

参考资料

转载地址:http://jqrfo.baihongyu.com/

你可能感兴趣的文章