读书笔记3:Javascript基本概念

进入到第三章内容,非常基础的部分,也很琐碎,整理起来确实有点难度,在脑海中难以连成一片。所以接下来的读书笔记,常见的、最显而易见的部分尽量就不罗列了,挑一些我认为比较生冷的部分加以复习。

思维导图比较杂乱,可粗看。
这里写图片描述

语法

语法部分我认为标识符和严格模式这两块可以理解一下的。

(1)标识符

标识符的命名规则值得学习,而且要求自己做到规范,以下几点,整理于本书及网上资源:

  • 第一个字符必须是一个字母/下划线/美元符合;
  • 其他字符可以是字母/下划线/美元符号/数字;
  • 不允许包含空格和其他标点符号(文件名/目录名也不要用空格,虽然在windows系统是合法的,但别的系统中可能会出错);
  • 不推荐使用特殊字符,那怕是合法的;
  • 推荐使用驼峰法:一般使用小驼峰法(lower camel case),类名等使用大驼峰(upper camel case),常量可使用全大写;
  • 变量命名长度应该尽可能的短,并抓住要点,尽量在变量名中体现出值的类型;
  • 命名尽量语义化,尽量用英语语义,拼音有点low,尽量避免使用没有意义的命名;
  • 常用的词语可用缩写
缩写全称及含义缩写全称及含义
infoinformation 信息create创建
impimportant重要的fnfunction函数
initinitialization初始化、最初的update修改
deldelete 删除slctselect查询选择
rmremove移除query获取
add增加get获取
insert插入concontent内容
(2)严格模式

严格模式 是在ECMAScript 5( 即 JavaScript 1.8.5 )中添加的,在旧版JavaScript本中会被忽略。现在开发中,推荐使用严格模式,可以消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为,它的好处总结为三点:

  • 消除代码运行的一些不安全之处,保证代码运行的安全
  • 提高编译器效率,增加运行速度
  • 为未来新版本的Javascript做好铺垫

严格模式具体包含了以下的一些限制:

  • 不允许使用未声明的变量;
  • 不允许用delete方法删除变量、对象或函数;
  • 不允许函数的形参重名;
  • 不允许使用八进制;
  • 不允许使用转义字符;
  • 不允许对只读属性赋值,非严格模式下可执行赋值,但不会生效;
  • 不允许对一个使用getter方法读取的属性进行赋值;
  • 变量名不能使用 “eval” 和”arguments” 字符串;
  • 在作用域 eval() 创建的变量不能被调用;
  • 禁止的this关键字指向全局对象;使用构造函数时,如果忘了加new,this不再指向全局对象,而是报错。

补充一个关于对象只读属性的知识:
实际上对象的数据属性包括以下4项,使用 Object.defineProperty() 方法定义函数属性时需要指定这些属性
value 对象属性的默认值,默认值为 undefined
writable 对象属性是否可修改,false 为不可修改,默认值为 true
enumerable 对象属性是否可通过 for-in 循环,false 为不可循环,默认值为 true
configurable 能否使用 delete、能否需改属性特性、或能否修改访问器属性,false 为不可重新定义,默认值为 true
来源于大神博客

变量

文中提到 “ ECMAScript 的变量是松散类型的” 。没有讲的很透彻,所以我在网上搜索了一些资料,主要有以下几点:

  • 可以用来保存任何值,保存任何类型的值;
  • 可以在修改变量值的同时修改值的类型;
  • 可以直接初始化变量,即在定义变量的同时可以设置变量的值;
  • 局部作用域中定义的变量会在函数退出后被销毁(因此,定义变量时,务必使用 var 或 let 操作符,以免局部变量成为全局变量造成混乱)。

补充知识点:js代码会有一个预加载的过程,预加载的目的是要事先构造运行环境例如全局环境,函数运行环境,还要构造作用域链,而环境和作用域的构造的核心内容就是指定好变量属于哪个范畴,因此在javascript语言里变量的定义是在预加载完成而非在运行时期。
来源于大神博客

数据类型

这部分内容很多很散,我梳理几个重要的知识点

(1)已声明未初始化变量和未声明变量
  • 执行 typeof 操作符都会返回 undefined
  • 已声明未初始化变量的值是 undefined 、数据类型也是 undefined
  • 未声明变量的数据类型是 undefined
  • 可以使用已声明未初始化变量,但使用未声明变量会报错;
  • 实际开发中,尽量显式地初始化变量,这样有助于区分未声明变量。
(2)关于NaN
  • NaN,非数值,Not a Number;
  • 任何涉及NaN的操作,都会返回NaN;
  • NaN与任何值都不相等, 包括本身;
  • isNaN()函数检查一个值是不是数值。
(3)数值转换
  • Number() 可以用于任何数据类型;
  • parseInt()parseFloat() 专门用于把 字符串 转换为整数数值或浮点数值;
  • 使用 parseInt() 时小数点不是有效的数字字符,因为是整数;而使用 parseFloat() 时,第一个小数点是有效浮点数字字符,第二个无效;
  • parseInt() 支持十六进制和八进制; parseFloat() 只解析十进制值,十六进制格式转换为0;
  • parseInt() 第二个参数可以指定进制解析字符串,支持二进制、八进制、十六进制。为避免错误解析,建议无论什么情况下都明确指定基数,一般指定10为基数。
(4)String类型相关
  • 一个转义字符表示一个字符,占一个长度
let str = '\u4eac'; // 文本 “京”
console.log(str.length); //输出 1
  • 数值、布尔值、对象、字符串都有 toString() 方法,nullundefined 值没有这个方法,会报错;
  • 数值的 toString() 方法可以传递参数——输出数值的基数,支持二进制、八进制、十六进制,默认十进制;
  • String() 函数能够将任何类型的值转换为字符串;
(5)Object原型的属性和方法
  • 构造函数 constructor:保存着用于创建当前对象的函数;
  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(不是在实例原型中)是否存在;
  • isPrototypeOf(object):用于检查传入的对象是否是当前对象的原型(原型的概念会在后面的章节中提及);
  • propertyIsEnumerable(propertyName):检查给定属性是否能够使用for-in语句来枚举;
  • toLocaleString():返回对象的字符串表示,与执行环境的地区对应;
  • toString():返回对象的字符串表示;
  • valueOf():返回对象的字符串、数值或布尔值表示,通常与 toString() 方法返回值相同。

操作符

我梳理几个重要的知识点

(1)操作符和函数及它们的区别

操作符,是在表达式中用于连接不同对象的运算符,不同的操作符指定了不同的运算方式。
函数,是一组一起执行一个任务的语句。

  • 可以把操作符理解为语言内置的,最基础的函数,不可代替的函数;
  • 操作符本质上也是函数,只是操作符是编译器需要进行进一步解释,而函数是直接调用;
  • 操作符只能重载,不能自定义,函数的名字随便起,只要是个标识符就行;但操作符不行;
  • 函数本身有一段代码,程序执行时,遇到函数时,会先将函数的参数入栈,再跳到函数的代码来运行。而操作符则是在本地直接运算。
(2)递增递减操作符前置和后置的区别
  • 执行前置递增递减操作时,变量的值都是在语句被求值以前改变的;
  • 后置递增递减操作是在包含它们的语句被求值之后才执行的。理解为其他操作执行完之后,再执行递增递减操作。

例1:递减操作符 前置

var num1 = 2;
var num2 = 20;

var num3 = --num1 + num2;   //等于 21 
                            //此行代码执行过程中,--num1 已经执行,此时 num1 为1

var num4 = num1 + num2;     //等于 21
                            //此时 num1 是 1 

例2:递减操作符 后置

var num1 = 2;
var num2 = 20;

var num3 = num1-- + num2;   //等于 22 
                            //此行代码执行过程中,num1 仍是 2,执行完之后才计算 num1--

var num4 = num1 + num2;     //等于 21
                            //此时 num1 已经是 1 了
(3)逻辑与的返回规则
  • 第一个操作数是对象,返回第二个操作数
  • 第二个操作数是对象,只有第一个操作数求值结果为true的情况下,才会返回该对象
  • 两个操作数都是对象,则返回第二个操作数
  • 第一个操作数是null,返回null
  • 第一个操作数是NaN,返回NaN
  • 第一个操作数是undefined,返回undefined
    逻辑与属于短路操作,如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。所以,第一个操作数是 false,则无论第二操作数是什么值,结果都不再可能是true了。
(4)逻辑或返回规则
  • 第一个操作数是对象,返回第一个操作数
  • 第一个操作数的求值结果为false,返回第二个操作数
  • 两个操作数都是对象,返回第一个操作数
  • 两个操作数都是null,返回null
  • 两个操作数都是NaN,返回NaN
  • 两个操作数都是undefined,返回undefined
    逻辑或也是短路操作符,第一个操作数的求值结果为 true时,就不会对第二个操作数求值了。
    开发中一个常用的技巧:给变量赋值时,可以使用逻辑或,避免赋 nullundefined值,相当于提供一个后备值。
(5)字符串比较
  • 比较字符串时,会比较两个字符串对应的字符编码值;
  • 两个以字符串保存的数字字符串,比较时会依照字符编码值比较,出现意外的结果,所以应先转为数值类型;
 var result1 = "23 < "3"; //true 结果不符合预期
 var result2 = "23" < 3; //false

 //情况1中,因为两个操作数都是字符串,会比较它们的字符编码,
 //“2”的字符编码是50,“3”的字符编码是51,因此会出现这种异常。
(6)运算符优先级

这部分书中没有提及,但我认为是很重要的,MDN有最权威的资料。
访问MDN

函数

我梳理几个重要的知识点

(1)理解函数的参数
  • 函数体内可以通过 arguments 对象访问参数数组;
  • arguments 对象与数组类似,可以获取长度,可以使用方括号语法访问元素,但它不是数组的实例;
  • 命名的参数只是提供便利,但不是必需的。没有命名参数,可以通过 arguments 访问传入的参数;
  • arguments 的值,永远与对应命名参数的值保持同步。但是,他们的内存空间是独立,并不是访问同一个内存空间;
  • arguments 的长度是由传入的参数个数决定的,不是由定义函数时的命名参数个数决定的;
  • 没有传递值得命名参数将自动赋予 undefined 值,相当于定义了变量但又没有初始化。
(2)JavaScript函数没有重载

重载的概念
就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。

  • JS没有重载,两个名字相同的函数,该名字只属于后定义的函数;
  • 通过检查传入函数中参数的类型和数量,并做出不同反应,可以模仿方法的重载。

贴一个模仿重载的案例,来源于大神博客

例1: JS 函数需要实现重载的话,可以根据 arguments 对象的 length 值进行判断

function overLoading() {
  // 根据arguments.length,对不同的值进行不同的操作
  switch(arguments.length) {
    case 0:
      /*操作1的代码写在这里*/
      break;
    case 1:
      /*操作2的代码写在这里*/
      break;
    case 2:
      /*操作3的代码写在这里*/
      break;
       
      //后面还有很多的case......
    };
};

例2: 利用闭包函数的特性,根据根据 arguments 对象的 length 值进行选择方法


//people对象,里面存着一些人名
//外国人名字组成包括first-name和last-name,就相当于我们的姓名由姓和名组成一样
var people = {
  values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};

/**
 * 我们希望people对象拥有一个find方法,
 * 当不传任何参数时,就会把people.values里面的所有元素返回来;
 * 当传一个参数时,就把first-name跟这个参数匹配的元素返回来;
 * 当传两个参数时,则把first-name和last-name都匹配的才返回来。
 * find方法是根据参数的个数不同而执行不同的操作的
**/

//addMethod
function addMethod(object, name, fn) {
  var old = object[name]; //把前一次添加的方法存在一个临时变量old里面
  object[name] = function() { // 重写了object[name]的方法
    // 如果调用object[name]方法时,传入的参数个数跟预期的一致,则直接调用
    if(fn.length === arguments.length) {
      return fn.apply(this, arguments);
    // 否则,判断old是否是函数,如果是,就调用old
    } else if(typeof old === "function") {
      return old.apply(this, arguments);
    }
  }
}


/* 下面开始通过addMethod来实现对people.find方法的重载 */

// 不传参数时,返回peopld.values里面的所有元素
addMethod(people, "find", function() {
  return this.values;
});

// 传一个参数时,按first-name的匹配进行返回
addMethod(people, "find", function(firstName) {
  var ret = [];
  for(var i = 0; i < this.values.length; i++) {
    if(this.values[i].indexOf(firstName) === 0) {
      ret.push(this.values[i]);
    }
  }
  return ret;
});

// 传两个参数时,返回first-name和last-name都匹配的元素
addMethod(people, "find", function(firstName, lastName) {
  var ret = [];
  for(var i = 0; i < this.values.length; i++) {
    if(this.values[i] === (firstName + " " + lastName)) {
      ret.push(this.values[i]);
    }
  }
  return ret;
});

// 测试:
console.log(people.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(people.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(people.find("Dean Edwards")); //["Dean Edwards"]

对于例2,很绕,我的理解是:addMethod 方法向 people.find 返回了一个闭包匿名函数,一个重要的参数 fn 都还保留在内存中(三次调用的都在,仍然可以访问得到);调用 people.find 的时候,会进行参数长度的比较,进而调用对应的参数。


以上,就是《Javascript高级程序设计》第三章的学习笔记。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值