JavaScript语言精粹

·  阅读 678

第一章 精华

1.1 认识JavaScript

  • JavaScript函数是基于词法作用域的顶级对象(javascript中的额函数是根据词法来划分作用域的,而不是动态划分作用域)
  • JavaScript是一门弱类型语言
  • JavaScript有非常强大的字面量表示法(这种表示法也是JSON的灵感来源)
  • 原型继承是JavaScript有争议的特性

1.2 为什么要学习JavaScript

  • 1) 你没得选择。web已经变成了重要的开发平台,JavaScript是唯一一门所有浏览器都可以识别的语言。
  • 2) 他尽管有缺陷,但是真的很优秀。具有极强的表现力的轻量级语言,掌握以后,会爱上函数式编程。

第二章 语法

2.1 空白

空白表现为被格式化的字符或者注释。

  • 1、空白通常是没有意义的,但有时候必须要用它来分割字符序列,否则他们会合并成一个字符。

var a = 2 比如,var和a之间的空格是不能移除的,其他的可以被移除。

  • 2、javascript提供两种注释,一种是包围的块注释 /* */ 一种是行注释 //。块注释对于被注释的代码而言是不安全的,尤其是遇到正则表达式的时候。

/* var a=/a*/.match(s) */

2.2 标识符

  • 首字母必须是字母、下划线_或美元符号$
  • 不能是数字。除首字母外,其他字符可以是字母、数字、下划线_或美元符号$,
  • 普通标识符不能是保留字
  • 标识符被用作于语句、变量、参数、属性名、运算符和标记。

2.3 数字

  • 1)JavaScript只有一个数字类型。内部表现为64位的双精度的浮点数(8字节)。第63位为符号位。52-62为指数位,52-0为为小数字。

  • 2)与其他语言不同的是JavaScript 没有分离出整数类型,所以11.0是相等的。
  • 3)完全避免了短整型被溢出问题
  • 4)1000=1e3(1* 10三次方)
  • 5)负数可以用 -和数字构成
  • 6)NaN是一个数值,他表示不能产生正常结果的数值
  • 7)JavaScript拥有对象Math,作用于数字的方法,例如Math.abs(-8) //8

2.4 字符串

  • 字符串可以是包含在''或者""中的0个或者多个字符。\是转义字符,JavaScript在被创建的时候,Unicode是一个16位的字符集,所以JavaScript所有字符都是16位的。
  • JavaScript没有字符类型,要表示一个字符,只需创建包含一个字符的字符串就可以。
  • 字符串有length属性。例如:"hello world".length //11
  • 字符串是不可变得,一旦创建它,就永远无法改变它。
  • 两个包含着完全相同的字符,且字符顺序也是相同的字符串是相等的'a'+'b'==='ab' //true
  • 字符串也包含一些方法。'aB'.toLocaleLowerCase() //ab

2.5 语句

  • 一个编译单元包含一组可执行的语句
  • var用在函数内部,定义的是函数的私有变量
  • 语句通常是从上向下执行,JavaScript利用条件语句、循环语句、强制跳出语句(breakreturnthrow)和函数调用来改变执行序列。
  • 代码块通常是包含在{}里面的一组语句,不同于其他语言,JavaScript的代码块没有块级作用域。
  • 假值,falsenullundefinned空字符串数字0数字NaN
  • 其他值都是真值,包括true字符串“false”、以及所有对象
  • switch语句执行一个多路分支,它把其表达式和所指定的case进行比较,找到一个精确的匹配时,执行case中的从句,如果没有找到对应的匹配,则执行可选的default语句。防止继续执行下一个case,case从句后面应该跟一个break。
  • while语句执行一个简单的循环,如果表达式为false,则终止循环。
  • for语句有两种形式 for(var i=0;i<arr.length;i++) 包含三个可选从句,初始化从句、条件从句、增量从句。另一种形式for(var i in arr)
  • do语句和while相似,唯一区别是do先执行语句后检测表达式的值,这意味着,do语句的代码块至少执行一次。
  • try语句执行一个代码块,并捕获该代码块执行的异常。catch从句定义一个新的变量varibale来接受这个异常。

2.6 表达式

1、最简单的表达式是

  • 字面量值(比如字符串或者数字)
  • 变量
  • 内置的值(true、false、null、undefined、NaN、Infinity)
  • 以new开头的调用表达式
  • 以delete开头的属性提取表达式
  • 包在圆括号中的表达式
  • 以一个前置运算法作为前导的表达式
  • 一个表达式 + 一个中置运算符与另一个表达式、
  • 一个表达式 + 三元运算符?表达式:表达式
  • 一个表达式 + 函数的调用
  • 一个表达式 + 一个属性提取表达式

2、运算符具有优先级,如下图所示,

  • ! 取非运算符,运算数为真的时候产生false,否则产生true
  • +可以进行加法运算,也可以进行字符连接。如果作为加法运算,左右操作数必须都是数字类型
  • / 除法运算符,2/3 结果:0.66666666~
  • && 逻辑与,如果左边运算数数假则取左运算数的值,否则取右运算数的值true&&3 //3
  • || 逻辑或,如果左边运算数数真则取左运算数的值,否则取右运算数的值true||3 //true
  • 函数调用引发函数执行。函数调用运算符是紧跟在函数名后面的一对圆括号。圆括号中可以包含传递给这个函数的参数。

3、typeof运算符产生的值numberstringbooleanundefinnedfunnctionobject。遇到数组或者null的时候,结果为object,这是不对的。

typeof null //object
typeof [1,2,3]  //object
复制代码

2.7 字面量

对象字面量是一种可以方便地按照指定规格创建新对象的表示法。属性名可以是标识符或者字符串。这些名字被当作字面量来对待,而不是变量名。所以对象的属性名在编译时才能知道。属性的值就是表达式。 数组字面量是一种可以方便地按照指定规格创建新数组的表示法。 后面会详细介绍!

2.8 函数

函数字面量定义了函数值。它可以有可选的名字用于递归调用自己。可以指定一个参数列表,这些参数就像变量一样,由调用时传递实际的参数来初始化。函数的主体包括变量定义和语句。

对象

  • JavaScript的简单数据类型包括数字字符串布尔值undefinednull
  • 其他所有值都是对象。数组、函数、正则表达式、包括对象自己都是对象。
  • 数字字符串布尔值 “貌似”对象,因为他们拥有方法,但他们是不可变的。对象是可变的键控集合。
  • 对象是属性的容器,其中每个属性都拥有名字和值。属性名可以包括空字符串在内的任意字符串。属性值可以是undefined之外的任意值。
  • JavaScript里面的对象是无类型的。他对新属性的名称和值没有限制(也就是说对象中可以包含多种类型,也可以包含对象)

3.1 对象字面量

对象字面量提供一种非常方便地创建新对象值的表示法。对象字面量可以出现在任何允许表达式出现的地方。

 var obj={}
	
var MyMessage={
   name:'vina',
   age:13,
   hobby:{
 	Game:'King glory',
 	Movement:["running", "swimming", "badminton"],
  }
}
复制代码

注意:属性名是合法且非保留字,不需要“”,如果是连接符-连接的则不合法,必须括起来。例如"a-b",下画线_连接是合法的!

3.2 检索

检索对象的值,使用这种[表达式]方式,如果字符串表达式是合法的且非保留字,我们也可以使用可读性更好、视觉更紧凑的.来替代[]

MyMessage["hobby"]
MyMessage.hobby
复制代码

如果尝试检索一个不存在的值,返回undefined。我们可以使用||来填充默认值

MyMessage.hobby||'-'   //如果检索不到,就返回 `-`
复制代码

但是如果我们尝试从undefined中再获取属性值就会导致TypeError异常。这时候我们可以通过&&运算符避免错误

MyMessage.hobby2.age  //VM780:1 Uncaught TypeError: Cannot read property 'age' of undefined
MyMessage.hobby2&&obj2.hobby2.age  //undefined
复制代码

3.3 更新

对象里面的值可以通过赋值语句来更新。如果已经存在会被替换,没有存在会被扩充进去。

 MyMessage.age=18  //覆盖原来的age值
 MyMessage.level=3 //level属性以及属性值会被扩充到MyMessage对象中
复制代码

3.4 引用

对象通过引用来传递,永远不会被复制

  var a={}
  var b={} 
  a==b   //false
  
  var a=b={}
  a==b //true
复制代码

3.5 原型

  • 每个对象都连接到一个原型对象,并且它可以从中继承属性。
  • 所有通过对象字面量创建的对象都链接到Object.prototype,他是JavaScript中的标配对象。
  • 当你创建新对象时,你可以选择某个对象作为它的原型。
  • 当我们对一个对象做出更新时,不会触及该对象的原型。
  • 原型连接只有在检索的时候才会被用到。我们尝试获取某个对象的属性时,但该对象没有该属性的,JavaScript会试着从原型对象中获取。如果原型对象也没有,那么再从这个原型对象的原型中寻找,直到找到Object.prototype。如果依然没有找到,会返回undefined。这个过程被称为委托
  • 原型关系是一种动态的关系,如果我们在原型对象中添加属性,那么该属性会立即对所有基于该原型创建的对象可见。

3.6 反射

var MyMessage={
      name:'vina',
      age:13,
      hobby:{
        Game:'King glory',
        Movement:["running", "swimming", "badminton"],
 }
 }
 typeof MyMessage.name  //"string"
 typeof MyMessage.hobby  //"object"
 typeof MyMessage.hobby.Movement  //"object"
复制代码

原型链中的任何属性都会产生值

typeof toString  //"funnction"
typeof constructor //"funnction"
复制代码

hasOwnProperty检查的是对象本身的属性,不会去检查原型链。

MyMessage.hasOwnProperty('hobby')  //true
MyMessage.hasOwnProperty('toString') //false
复制代码

3.7 枚举

//字面量对象自己的属性
var MyMessage={
    name:'vina',
    level:3,
    age:13,
    hobby:{
        Game:'King glory',
        Movement:["running", "swimming", "badminton"],
    },
    subjects:["Chinese", "math", "English"]
}
//原型中的熟悉
Object.prototype.class="MZC"
for(var item in MyMessage){
 console.log(item)
}
//结果包含原型中的属性
name
level
age
hobby
subjects
class
复制代码

使用for...in进行对象枚举,会列出所有属性,包括函数和原型,如果想要过滤掉不关心的属性,可以使用hasOwnProperty进行判断。以及使用typeof过滤到函数。

  • 通过for...in遍历,属性名出现的顺序是不确定的,因此要对任何可能出现的顺序有所准备。
  • 如果想要确保出现的顺序,我们应该避开使用for...in,创建好特定顺序的数组,使用for循环遍历替代for...in可以保证遍历出来属性的顺序就是数组排列的顺序,并且不需要担心会遍历出原型的属性。

3.8 删除

  • delete运算符可以用来删除对象中的属性。
  • 如果对象中包含该属性,使用delete可以移除对象中的这个属性。
  • 它不会触及原型中的任何属性
  • 删除对象可能会让原型中的属性透现出来。
var MyMessage = {
    name: 'vina',
    level: 3,
    age: 13,
    hobby: {
        Game: 'King glory',
        Movement: ["running", "swimming", "badminton"],
    },
    subjects: ["Chinese", "math", "English"]
}
//所有通过对象字面量创建的对象都链接到Object.prototype
Object.prototype.name='Richur'  //原型中添加name属性
console.log(MyMessage.name)  //vina
delete MyMessage.name
console.log(MyMessage.name)   //Richur
复制代码

3.9 减少全局变量污染

  • 全局变量削弱了程序的灵活性,应该避免使用
  • 最小化使用全局变量的方法之一就是为你的应用只创建一个全局变量

var myapp={} 该变量此时变成了你的应用容器。

  myapp.storage={
      first-name:'liu',
      last-namee:'wei',
  }
  myapp.Myessage={
      name: 'vina',
    level: 3,
    age: 13,
    hobby: {
        Game: 'King glory',
        Movement: ["running", "swimming", "badminton"],
    },
    subjects: ["Chinese", "math", "English"]
  }
复制代码
  • 只要把全局变量都容纳在一个变量中,你的程序和其他应用程序组件库类库之间发生冲突的可能性会变得非常小。
  • 你的程序会变得更加易读。

第四章 函数

  • JavaScript最出色的就是它的函数的实现。近乎完美,略带瑕疵。
  • 函数包含一组语句,它们是JavaScript基础模块单元。用于代码的复用信息隐藏组合调用
  • 函数用于指定对象的行为
  • 所谓变成,不过就是将一组需求拆成一组函数和数据结构的技能。

4.1 函数对象

  • JavaScript中函数就是对象
  • 对象是“名/值”的集合并且拥有一个连接到原型的隐连接。
  • 对象字面量产生的对象连接到 Object.prototype
  • 函数对象连接到 Function.prototype
  • 因为函数是对象,所以它们可以像任意其他值一样被使用。
  • 函数可以保存在变量对象数组中。
  • 函数可以被当作参数传递给函数,函数也可以再返回函数。
  • 因为函数是对象,所以函数可以拥有方法。
  • 函数的不同之处在于它们可以被调用

4.2 函数字面量

函数对象通过函数字面量来创建

var add=function(a,b){
   return a+b
}
add(3,4)
复制代码

函数字面量包括四个组成部分。

  • 第一部分是保留字function
  • 第二部分是函数名(可省)。函数可以用它的名字递归的调用自己。此名字可以被开发者用来调试和识别函数。如果没有函数名字,例如上述例子中的函数,被称为匿名函数
  • 第三部分是包围在圆括号中的一组参数。多个参数用逗号隔开。它们被定义为函数中的变量,他们不像普通变量那样被初始化为undefined,而是在该函数被调用时初始化为实际提供的参数的值。
  • 第四部分是包含在花括号中的一组语句。这些语句是函数的主体,他们在被调用的时候执行。
  • 函数字面量可以出现在任何表达式可以出现的地方。
  • 函数也可以被定义在其他函数中,一个内部函数可以访问自己的参数和变量,同时也能自由的访问把它嵌套在内的父函数的变量和参数。通过函数字面量创建的函数对象包含一个连到外部上下文的连接,这被成为闭包。他是JavaScript强大表现力的来源。

4.3 调用

  • 调用一个函数会暂停当前函数的执行,传递控制权和参数给新的函数。
  • 除了声明时定义的形参,每个函数还接收两个附加的参数:this和arguments。
  • 参数this的值取决于调用的模式。JavaScript一共4种调用模式:方法调用函数调用构造器调用apply调用模式
  • 调用运算符是跟在任何产生一个函数表达式之后的一对圆括号。圆括号中可以包含0个或者个用逗号隔开的表达式。每个表达式产生一个参数值。
  • 当实际参数的个数和形式参数的个数不匹配的时候,不会导致运行时错误。参数过多被忽略,参数过少会用undefined替换。参数值不会进行类型检查,任何类型的参数值都可以传递给任何参数。

4.3.1 方法调用模式

当一个函数被保存为一个对象的一个属性时,我们称它为方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作(.[]),那么它就是被当作一个方法来调用的。

var myObject={
    name:'Vina',
    play:function(project){
        this.name="Richur"   //修改对象的属性值
        console.log(this.name+"正在玩"+project)  //获取对象的属性值
    }
}
myObject.play("game")
复制代码
  • 方法可以使用this访问自己所属的对象,所以他能从对象中取值或者对对象的值进行修改。
  • this到对象的绑定发生在调用的时候,使得函数可以对this高度复用。
  • 通过this可以取得它们所属对象的上下文的方法称为公共方法。

4.3.2 函数调用模式

当一个函数并非一个对象的属性时,那么他就是被当作一个函数来调用的。此模式调用函数时,this被绑定到全局对象

注意:这是一个在语言上设计的缺陷,倘若设计正确,当内部函数被调用时,this应该绑定带为不含念书的this变量。这个设计导致方法不能利用内部函数来帮助它工作,因此内部函数的this被绑定错误的值。所以不能共享该方法对对象的访问权。幸运的是,该方法可以定义一个变量并且把它赋值为this,那么内部函数可以通过那个变量访问到this。我们一般将这个变量约定为that

var myObject={
    value:0,
    increment:function(inc){
        this.value+=typeof inc === 'number'?inc:1
    }
}
myObject.increment();
document.writeln(myObject.value)  //1
myObject.increment(3);
document.writeln(myObject.value)  //4
var add=function(a,b){
    return a+b
}
myObject.double=function(){
    var that=this  //解决方法
    var helper=function(){
        that.value=add(that.value,that.value);  
    }
    helper()
}
myObject.double()
document.writeln(myObject.value)  //8
复制代码

4.3.3 构造器调用模式

  • JavaScript是一门基于原型继承的语言。这意味着对象可以直接从其他对象继承属性。
  • 这偏离了当今编程语言的主流风格。当今大多数语言都是基于类的语言。
  • 虽然JavaScript原型继承极富有表现力,但是他没有被广泛理解
  • JavaScript本身对原型的本质也缺乏信心,所以推出一套和基于类相似的对象构建语法。
  • 如果一个函数前面带了new来调用,那么背地里将会创建一个链接到该函数的prototype成员的对象,同时this也会被绑定到新的对象上。
//不推荐这种形式的构造器函数
var Fun=function(string){
    this.status=string
}
Fun.prototype.getStatus=function(){
    return this.status
}
var myFun=new Fun("vina")
console.log(myFun)   //{status:"vina"}
复制代码

如果一个函数创建的目的就是为了结合new使用,那么他就被成为构造器函数。 构造函数命名约定大写,防止调用构造器的时候没有加new

4.3.3 Apply调用模式

  • 因为JavaScript是一门函数式的面向对象的编程语言,所以函数可以拥有方法。
  • apply方法让我们构建一个参数传递给调用函数。它允许我们选择this值。
  • apply方法接受两个参数,第一个是绑定给this的值,第二个就是一个参数数组
var add=function(a,b){
    return a+b
 }
 var array=[3,5]
 var sum=add.apply(null,array)
 console.log(sum)  //8
复制代码

statusObject并没有继承Fun.prototype,但是我们可以在statusObject上调用getStatus,虽然statusObject没有getStatus方法。

var Fun=function(string){
    this.status=string
}
Fun.prototype.getStatus=function(){
    return this.status
}
var statusObject={
    status:'AAA_OK'
}  
var status=Fun.prototype.getStatus.apply(statusObject)
console.log(status)   //'AAA_OK'
复制代码

4.4 参数

当一个函数被调用时,会有一个arguments参数,这是一个数组。函数可以通过这个参数访问所有它被调用时传递给他的参数列表,包括余参(实参大于形参的部分)。这样我们就可以无需制定函数参数个数。

var sum=function(){
    var i,res=0
    for(i=0;i<arguments.length;i++){
        res+=arguments[i]
    }
    return res
}
console.log(sum(1,2,3,4,5,6,7,8,9))
复制代码

注意:arguments并不是一个真正的数组。他只是一个“类似数组”的对象,arguments拥有length属性。但是他没有数组的任何方法。

4.5 返回

  • 当return被执行时,函数立即返回不会执行余下的语句。
  • 一个函数总会返回一个值,没有指定返回值的返回undefined
  • 如果函数调用时前面加了new前缀,且返回值不是一个对象,则返回this(该新对象)
var Fun=function(string){
   this.status=string
   return
}
var fun=new Fun("app")
console.log(fun)
复制代码

4.6 异常

  • 如果try代码块抛出一个异常,控制权就会跳转到它的catch从句。
  • 一个try语句只会有一个catch代码块来捕获所有异常。可以根据异常对象的name来判断异常类型。
var add=function(a,b){
    if(typeof a !=='number'){
        throw{
            name:"aTypeError",
            message:'请重新输入a'
        }
    }else if(typeof b !=='number'){
        throw{
            name:"bTypeError",
            message:'请重新输入b'
        }
    }
    return a+b
}

var try_it=function(){
    try{
        add("a",2)
    }catch(e){
        console.log(e)
    }
}
try_it()  //VM259:20 {name: "aTypeError", message: "请重新输入a"}
复制代码

4.7 扩充类型的功能

我们可以对函数、数组、字符串、数字、正则表达式和布尔值扩充功能。

  • method方法对所有函数都可用
Function.prototype.getName=function(name){
    console.log(name)
}
function add(a,b){
   return a+b
}
add(2,3)
add.getName("vina")
复制代码

给对象增加方法以前是Function.prototype.方法名。调用.method就可以可以了。

Function.prototype.method = function (name, fun) {
    this.prototype[name] = fun
    return this
}

//字符串去空
Number.method('integer', function () {
    console.log("this", this) //Number{5}
    return Math[this < 0 ? 'ceil' : 'floor'](this)
})
console.log((-10 / 2).integer())

//字符串去空
String.method('trim',function(){
    return this.replace(/^\s+|\s+$/g,'')
})
console.log("   hahahha    ".trim())
复制代码

通过给基本类型增加方法可以大大提高语言的表现大,因为原型继承的动态本质,新的方法立刻被赋予到所有值上,哪怕值的创建比方法早。

Function.prototype.method = function (name, fun) {
    if (!this.prototype[name]) {
        this.prototype[name] = fun
        return this
    }else{
        console.log("这个方法名已经存在,请重新创建")
    }
}
复制代码

4.8 递归

递归函数是直接或者间接的调用自己。递归是一种强大的编程技术,将一个问题分解成多个子问题去解决。一般来说,递归函数调用自身去解决它的子问题。

4.9 作用域

  • 作用域控制变量的可见性和生命周期,它可以减少名称冲突并且提供了自动内存管理。
  • 代码块中定义的变量在代码块外面是不可使用的。JavaScript不支持代码块。
  • JavaScript确实有函数作用域。所以在函数中定义的变量在函数外是不可见的。
  • 很多现代语言提倡尽可能迟的声明变量。但是JavaScript缺少块级作用域,在函数体的顶部尽可能声明所有会在函数中用到的变量。

4.10 闭包

  • 作用域的好处是内部函数可以使用定义在他们外部函数的参数和变量(除了this和argument)。
  • 内部函数拥有比他外部函数更长的生命周期。

4.11 回调

4.12 模块

4.13 级联

4.14 柯里化

4.15 记忆

——————————————————————————持续学习,未完待续

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改