JavaScript之ECMAScript篇

75 阅读14分钟

语言

  1. 编译性语言:通篇翻译,完成后生成一个翻译完的文件,程序会执行这个翻译完的文件。
  • 优点:通篇翻译快;
  • 缺点:移植性不好(不跨平台)。
  1. 解释性语言:翻译一行执行一行。
  • 优点:跨平台;
  • 缺点:翻译一行执行一行“稍微慢”。

总结:

  1. JavaScript是解释性语言。
  2. 单线程(异步):一个执行体同一时间内只能做一件事。
  3. 多线程(同步):一个执行体同一时间内能做多件事。
  4. JavaScript引擎是单线程。
  5. 计算机里面的同步叫异步,计算机里面的异步叫同步。
  6. JavaScript执行队列(轮转时间片)
  7. JavaScript三大部分:ECMAScriptDOMBOM

主流浏览器及其内核

  • IE(trident)
  • Chrome(webkit/blink)
  • firebox(Gecko)
  • Opera(prsto)
  • Safari(webkit)

变量

  1. 变量声明、赋值
var a;
  1. 命名规则
  • 变量名必须以英文字母、_、$开头
  • 变量名可以包括英文字母英文字母、_、$、数字
  • 不可以用系统的关键字、保留字作为变量名

数据类型

  1. 基本数据类型
  • 原始值(stack栈)
number、boolean、string、undefined、null

var a = 10;
var b = a;
a = 20;
console.log(a,b) // 20,10
  • 引用值(heap堆)
arr、obj.......

var arr = [1,2];
var arr1 = arr;
arr.push(3);
console.log(arr,arr1) //[1,2,3],[1,2,3]

var arr = [1,2];
var arr1 = arr;
arr = [1,3];
console.log(arr,arr1) //[1,3],[1,2]
  1. js语句基本规则
  • 语句后面要分号结束
  • js语法错误会引发后续代码终止,但不会影响其它js代码块
  • 书写格式要规范,“=+/-”两边都应该有空格
  1. 错误分两种
  • 低级错误(语法解析错误): SyntaxError(语法解析错误)、ReferenceError(引用错误)
  • 逻辑错误(标准错误,情有可原)

js基本运算符

+:数学运算,字符串拼接 
-、*、/、%(摩尔)取余、=、()
0 / 0 :NAN(数字类型)
1 / 0 :Infinity(正无穷)
-1 / 0 :-Infinity(负无穷  )
优先级 = 最弱 ()较高
++、--、+=、-=、*=、/=、%=
a++自身加加在赋值给自己
a--自身减减在赋值给自己

var a = 10
console.log(a ++) // 10 
console.log(a) // 11
++在后:先执行语句后++
console.log(++ a) // 11
console.log(a) // 11
++在前:先++在执行本条语句

var b = ++a - 1 + a++
console.log(b,"+",a) // 21,12

%=:取余付给自身 

条件运算符

  • NAN不等自己,包括自己
  • 字符串比较的ASCLL
  • <、>=、<=、==、!=、>

逻辑运算符

  1. && || !
  • (&&)规则
  • 先看第一个表达式如果转换成布尔值的结果,如果为真,那么它会看第二个表达式转化为布尔值的结果,然后如果只有两个表达式的话,只看第二个表达式,就可以返回该表达式的值了。
  • 先看第一个表达式如果转换成布尔值的结果,如果为假,直接返回结果
  • (||)规则
  • 先看第一个表达式如果转换成布尔值的结果,如果为假,那么它会看第二个表达式转化为布尔值的结果,然后如果只有两个表达式的话,只看第二个表达式,就可以返回该表达式的值了。
  • 先看第一个表达式如果转换成布尔值的结果,如果为真,直接返回结果
  • 注:undefinednullNAN,“”,0,false转化为布尔值为false

条件运算符

  1. if(){}
  • if语句中写&&的时候,必须全真才执行,||有一个为真就执行
  1. if(){}else{}
  2. for(){}
  3. while(){}
  4. switch(){}

typeof

  1. JavaScript的六种基本数据类型 undefiend < 0 // false undefiend == 0 // false
  • number、string、boolean、function、undefined、object
  1. 显示类型转换
  • Number(mix)转成数字
  • 特殊:true ==> 1 、false ==> 0 、null ==> 0 、undefined ==> NAN
  • parseInt(string,radix)
  • 特殊:parseInt(string,radix)有两个参数一个需要转换的,另外一个是radix它的范围是2-36
  • parseFloat(string)用法号parseInt(string)类似
  • toString()String()转成字符串
  • 特殊:undefined、nul不能使用toString()
  • Boolean()转换成布尔值
  • 特殊:undefined、null、NAN、“”、0、false转成布尔值为false
  1. 隐式类型转换
  • isNAN() ==>Number()
  • ++--、+- ==> Number()
    • ==> String() 如果加号一边有字符串就会调用String()
  • -、*、%、\ ==> Number()
  • &&、||、! ==> Boolean()
  • <、>、>=、<= ==> Boolean()
  • ==、!= ==> Boolean()
  • toFixed()保留几位有效数字
undefined > 0 // false
undefiend < 0 // false
undefiend == 0 // false

null > 0 // false
null < 0 // false
null == 0 // false

null == undefined // true

NAN == NAN // false

  1. 不发生类型转换
  • === 、!==
  • 注意:typeof(typeof(a)) ==> string

函数

  1. 定义
  • 三种形式
  • 函数声明
function test(){}
  • 命名函数表达式
var test = function abc(){}
  • 匿名函数表达式 -- 函数表达式
var test = function {}
  1. 组成形式
  • 函数名称
function sum(){}
  1. 参数
// 形参
function sum(a,b){
    // arguments 实参列表
    if(sum.length > arguments.length){
        console.log("形参多了")
    }else if(sum.length < arguments.length){
        console.log("实参多了")
    }else{
        console.log("实参和形参相等")
    }
}

// 实参
sum(1,2)

// 形参
function sum(){
    // arguments 实参列表
}
注意:实参和形参都可以多,函数不会受影响
  1. 返回值
  • return 终止函数

js三部曲

  1. 语法分析
  2. 预编译
  • 函数声明整体提升
  • 变量 声明提升
  1. 预编译前奏
  • imply global暗示全局变量:即任何变量,如果未经声明就赋值,此变量就为全局对象所有
  • 一切声明的全局变量,全是Windons的属性
  1. 预编译四部曲
  • 问题:一个变量包含形参名,变量名,函数名一样的问题。解决优先级顺序的问题,包括谁覆盖谁的问题,在包括执行顺序影响变量,函数等。
  • 运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
  • 查找变量:从作用域链的顶端依次向下查找。
  • 创建AO对象(执行期上下文)
  • 找形参和变量声明,并且把形参和变量的名作为AO对象的属性名,值统一为undefined
  • 实参和形参值 相统一
  • 在函数声明找函数声明,把函数声明的名当成AO对象的属性名,值被覆盖为函数体
  • 执行
function fu(a){
  console.log(a)
  var a = 13;
  console.log(a)
  function a(){}
  console.log(a)
  var b = function {}
  console.log(b)
  function d(){}
}
fu(a)
规律: 如何一个函数里有重名的变量和函数,并且在第一次打印,一定是函数,因为他的优先级最高
  1. 解释执行

作用域

  1. [[scope]]: 每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。
  2. [[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
  3. 作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
function a(){
	function b(){
		function c(){
		
		}
		c()
	}
	b()
}
a()

解释:
a 定义 --> 0: 生成GO

a 执行 --> 0: 生成aAO

           1: 生成GO

b 定义 --> 0: 生成aAO

           1: 生成GO

b 执行 --> 0: 生成bAO

           1: 生成aAO

           2: 生成GO

闭包

  • 什么是闭包:当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。
  • 作用:
  • 1、实现公有变量:累加器
function a(){
	var count = 0;
  function b(){
		count++
    console.log(count)
	}
  return b
}
var d = a();
d();
  • 2、可以做缓存(存储结构)
function eater(){
		var food = "";
  	var obj = {
				eat: function (){
						console.log("I am eat "+ food);
          	food = "";
				},
      	push: function (myFood){
          	food = myFood;
				}
		}
    return obj;
}
var eater1 = eater();
eater1.push("苹果");
eater1.eat()
  • 3、可以实现封装,属性私有化
function Person(name,age){
		var a = '1';
  
  	this.name = name;
  	this.age = age;
  	
  	this.add = function{
      this.age = a;
		}
  	this.changeAdd = function(newValue){
			a = newValue;
		}
  	this.sayAdd = function(){
      console.log(a)
		}
  	
}
var person = new Person('占三','19');
  • 4、模块化开发,防止污染全局变量

立即执行函数

什么是立即执行函数:此类函数没有声明,在一次执行过后即释放。适合做初始化工作。

(function (){

}()) // 第一种

(function (){

})() // 第二种

// 只有表达式才能被执行符号执行
执行完了,就销毁了,有返回函数,有执行期上下文,有预编译,函数有的它都有。

总结:什么是闭包?

  • 两个或多个函数嵌套,把里面的函数保存到外面,这样的一个情况必然会形成闭包,然后里面的函数在外面执行的时候,一定能调用了原来他在的函数环境里面的变量。
  • 当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放, 造成内存泄露。(官方解释)

对象

  1. 对象组成
  2. 对象的增删改查
  3. 对象的创建方法
1var obj = {} // plainObject 对象字面量
2、构造函数 
    (1)系统自带构造函数 new Object()
    (2)自定义
  1. 构造函数的内部原理
  • 在函数题最前面隐式的加上this = {}
  • 执行this.xxx = xxx;
  • 隐式的返回this
// 命名规则:大驼峰
function Student(name ,age ,sex){
		// var this = {}
		this.name = name;
		this.age = age ;
		this.sex = sex;
		// return this;
}
var stu = new Student('张三','19','男');
  1. 包装类
new Number()
new String()
new Boolean()

例子:
var num = 8;
num.len = 2;
// 这里使用到了包装类
/*
*new Number(num).len = 2
*delete
*
*new Number(num).len (访问)
*/
console.log(num.len) // undefined

var str = 'abcd';
str.length = 2;
// 这里使用到了包装类
/*
*new String(str).length = 2
*delete
*
*new String(str).length (访问)
*/
console.log(str) // abcd
console.log(str.length) // 4

原型

  1. 定义: 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。并且原型也是对象。
Person.prototype.lastName = "张三";
function Person(name,age,sex){
	this.name = name;
  this.age = age;
  this.sex = sex;
}
var person = new Person('王五','19','男');
person.lastName // "张三" 
person.prototype.lastName = '李四';
person.prototype.NewName = '程东';
delete person.prototype.lastName
  1. 利用原型特点和概念,可以提取共有属性。
// 共有属性
Person.prototype.height = 1400;
Person.prototype.weight = 1000;

function Person(color){
	this.color = color;
}
var person = new Person('black');
var person1 = new Person('red');
  1. 对象如何查看原型--->隐式属性__proto__
  2. 对象如何查看对象的构造函数--->constructor
function Person(){

}

function Car(){

}
Car.prototype = {
	constructor:Person

}
var car = new Car()
  1. 原型链
  • 如何构成原型链
  • 原型链上属性的增删改查
  • 只有本身原型上有的属性才可以增删改查,其他人继承的无法增删改查
  • 绝大多数对象最终都会继承自Object.prototype(重点需要记住)
  • Object.create() 8.callapply
function Person(name,age,sex){
	this.name = name;
        this.age = age;
        this.sex = sex;
}
function Student(name,age,sex,wight,height){
	// this.name = name;
        // this.age = age;
        // this.sex = sex;
        Person.call(this,name,age,sex);
        Person.apply(this,[name,age,sex]);
        this.wight = wight;
        this.height = height;
}
var stu = new Student('张三','18','男','180','90');

两者区别:

  1. callapply是做什么的,有什么区别:
  2. callapply是改变this的指向,传参列表不同
  3. call是需要把实参按照形参的个数传进去
  4. apply是需要穿一个arguments

继承

  1. 传统形式 ---> 原型链
## 过多的继承了没有用的属性
Grand.pototype.lastName = '张三';
function Grand(){
}
var grand = new Grand();

Father.pototype = grand;
function Father(){
  this.name = "李四";
}
var father = new Father();

Son.pototype = father;
function Son(){
}
var son = new Son();
  1. 借用构造函数
  • 不能继承借用构造函数的原型
  • 每次构造函数都要多走一个函数
function Person(name,age,sex){
	this.name = name;
  	this.age = age;
  	this.sex = sex;
}
function Son(name,age,sex,height){
  	Person.call(this,name,age,sex)
	this.height = height;
}
var son = new Son();
  1. 共享原理
  • 不能随便改动自己的原型
Person.pototype.lastName = '张三';
function Person(){
}
function Son(){
}
// Son.pototype = Person.pototype;// 封装成函数
function extends(newValue,OlbValue){
		newValue.pototype = OlbValue.pototype;
}
extends(Son,Father);

// 如果新增son属性,father也会新增属性
son.pototype.age = '18'; 
var son = new Son();
  1. 圣杯模式
Person.pototype.lastName = '张三';
function Person(){
}
function Son(){
}
// 基本写法
function extends(newValue,OlbValue){
  	function F(){};
  	F.pototype = OlbValue.pototype;
	newValue.pototype = new F();
  	newValue.pototype.constructor = newValue;
  	// 可以知道它继承谁
  	newValue.pototype.uper = OlbValue.pototype;
}
// 高级写法
var extends = (function (){
  	var F = function(){};
  	return function () {
	F.pototype = OlbValue.pototype;
	newValue.pototype = new F();
  	newValue.pototype.constructor = newValue;
  	// 可以知道它继承谁
  	newValue.pototype.uper = OlbValue.pototype;
    }
}());
extends(Son,Father);

// 如果新增son属性,father这时不会新增
son.pototype.age = '18'; 
var son = new Son();

命名空间

  1. 管理变量
  2. 防止污染全局
  3. 适用于模块开发
var init = (function(){
  var a = 123;
  function callNum(){
      console.log(a)
}
  return function(){
      callNum()
}
}())
  1. 如何模拟jQ连续调用
var obj = {
    read:function(){
	console.log('I am reading')
    return this;
	},
    sing:function(){
	console.log('I am singing')
    return this;
	},
    jump:function(){
	console.log('I am jumping')
    return this;
	}
}
obj.read().sing().jump()
  1. 属性表示方法
##obj.prop
##obj['prop']

var obj = {
	sum1:{name:'1'},
        sum2:{name:'2'},
        sum3:{name:'3'},
        sayNum:function(num){
            return this['sum' + mum]
	}
}
  1. 对象枚举
  • for in
var obj = {
	name : 'zhangsan',
  age : '19',
  sex : '男'
}
// 遍历枚举和对象使用 for in
for (var key in obj) {
	console.log(obj[key])
}
注:for xxx  in xxx 循环 就是遍历对象用。通过对象属性的个数遍历多少圈。遍历时把对象的属性明放到in前面的xxx里面去,
  1. hasOwnProperty判断对象的属性是否原型链上的
// in 和 hasOwnProperty的区别
// in的功能是判断你能不能用这个属性
// hasOwnProperty的功能是判读这个属性属于不属于该对象
var obj = {
	name : 'zhangsan',
  age : '19',
  sex : '男',
  __proto__:{
		lastName:'wangwu'
  }
}
for (var key in obj) {
  if(obj.hasOwnProperty(key)){
    console.log(obj[key])
	}
	
}
  1. instanceof
// 数组和对象的如何区分
instanceof的解释:
// A对象 是不是 B构造函数构造出来的
// 看A对象的原型链 有没有B的原型

方法一:
[].constructor // ƒ Array() { [native code] }
{}.constructor// ƒ Object() { [native code] }
方法二:
[] instanceof Array // true

{} instanceof Array // false

{} instanceof Object // true

方法三:
Object.prototype.toString.call([])
Object.prototype.toString.cal l({})
  1. this的指向
  • 函数预编译过程的this指向window
  • 全局作用域里this指向window
  • call/apply可以改变函数运行时this的指向
  • 谁调用函数,函数里面的this就指向谁
  1. arguments.calleefun.caller
function test(){
	console.log(arguments.callee);
  function demo(){
        console.log(arguments.callee);
	}
  demo() // demo
}
test() // test
  1. 克隆
// 浅克隆
var obj = {
  name:"张三",
  age:"19",
  sex:"男",
}
var obj1 = {}
function clone(origin,target){
	var target = target || {};
  for(var prop in origin){
		target[prop] = origin[prop]
  }
  return target;
}
clone(obj,obj1)

// 深拷贝
var obj = {
  name:"张三",
  age:"19",
  sex:"男",
  num:[1,2,3],
  wife:{
		son:{
			name:'小张'
    }
  }
}

var obj1 = {}

function deepClone(origin,target){
  // 容错
	var target = target || {},
  	toStr = Object.prototype.toString,
  	arrStr = '[object,Array]';
        for(var prop in obj){
        // 判断是否是原型链上的值
            if(origin.hasOwnProperty(prop)){
            // 判断是引用值
            if(origin[prop] !== 'null' &&
                 typeof(origin[prop]) == 'object'){
                 // 判断是数组还是对象
                 if(toStr.call(obj[prop]) == arrStr){
                  target[prop] = []
                 }else{
                  target[prop] = {}
                 }
                //递归
             deepClone(origin[prop],target[prop])}else{
		target[prop] = origin[prop]
	}
    }
}
  return target;
}

数组

  1. 创建数组
  • var arr = [](字面量)
  • var arr = new Array(length/content)
  • 注:var arr = new Array(10)表示数组长度为10
  1. 数组的读和写
  • arr[num] // 不可以溢出读 结果为undefined
  • arr[num] = xxx;// 可以溢出写
  1. 数组方法
  • 改变原数组:push、pop、shift、unshift、sort、reverse
var arr = [];
Array.prototype.push = function() {
	for(var i = 0; i < arguments.length; i++){
		this[this.length] = arguments[i]
  }
  return this.length;
}
reverse:数组反转(降序)

splice(从第几位开始,截取多少的长度,在切口处添加新数据)

sort数组排序(升序)
例子:
冒泡排序
1.必须写两个形参
2.看返回值
    (1)当返回值为负数时,那么前面的数放在前面
    (2)当返回值为正数时,那么后面的数放在前面
    (3)为0.不动
 
var arr = [1,3,2,4,6,4];
arr.sort(function(first,second){
  // 升序
	if(first > second){
    return 1;
	}else{
    return -1;
	}
  // 降序
  if(first < second){
    return -1;
	}else{
    return 1;
	}
})
// 通示 
var arr = [1,3,2,4,6,4];
arr.sort(function(first,second){
	return first - second; // 升序
  rerurn second - first; // 降 序
})

// 乱序
var arr = [1,3,2,4,6,4];
arr.sort(function(first,second){
  rerurn Math.random() - 0.5; 
})
  1. 不改变原数组:concat、join、split、toString、slice
  • 两个参数:slice(从该位开始截取,截取到该位) 截取
  • 一个参数:slice(从该位开始截取一直截取到最后)
  • 如果写负数:slice(负数 + length)
  • 不写参数:slice()全截取

类数组(必须写length)

var obj = {
	"0":'a',
  "1":'b',
  "2":'c',
  "3":'d',
  "length": 4,
  "push": Array.prototype.push
}

obj.push('e')
var obj = {
	"0":'a',
  "1":'b',
  "2":'c',
  "3":'d',
  "4":'e',
  "length": 5,
  "push": Array.prototype.push,
  "splice":Array.prototype.splice
}

例子:
var obj = {
  "2":'c',
  "3":'d',
  "4":'e',
  "length": 3,
  "push": Array.prototype.push
}

obj.push('a')
obj.push('b')
var obj = {
  "2":'c',
  "3":'a',
  "4":'b',
  "length": 5,
  "push": Array.prototype.push
}

原理:
Array.prototype.push = function(e){
	obj[obj.length] = e;
  obj.length++;
}

// 属性要为索引(数字)属性,必须有length属性,最好加上push
// 类数组的关键是length
// 好处

  1. 可以利用属性名模拟数组的特性
  2. 可以动态的增长length属性
  3. 如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充。

练习

1、封装`typeof`
function type(e) {
  // 1、原始值和引用值
  // 2、区分引用值
  if (e === null) {
    return null;
  }
  if (typeof e === "object") {
    if (Object.prototype.toString.call(e) === "[object Array]") {
      return "array";
    }
    if (Object.prototype.toString.call(e) === "[object Object]") {
      return "object";
    }
    if (Object.prototype.toString.call(e) === "[object Boolean]") {
      return "boolean - object";
    }
    if (Object.prototype.toString.call(e) === "[object Number]") {
      return "number - object";
    }
    if (Object.prototype.toString.call(e) === "[object String]") {
      return "string - object";
    }
  } else if(typeof e === 'function') {
    return 'function';
  } else {
    return typeof e;
  }
} 

function genrate (e, type = 'Array'){
  if (Object.prototype.toString.call(e) === `[object ${type}]`) {
      return true
    }
}

2、数组去重(原型链上数组去重)
Array.prototype.unique = function () {
  var temp = {};
  var result = [];
  for (var i = 0; i < this.length; i++) {
    if (!temp[this[i]]) {
      temp[this[i]] = true;
      result.push(this[i]);
    }
  }
  return result
}

总结:

  1. 一旦经历了var的操作,所得出的属性,window,这种属性叫做不可配置的属性。(通过varwindow增加的属性叫做不可配置属性)
  2. 不可配置的属性,delete删不掉

try catch

try{}catch{}finally{}

Error.name6种错误信息

  • EvalError:eval()的使用与定义不一致。
  • RanageError:数据越界。
  • ReferenceError:非法或不能识别的引用数值。
  • SyntaxError:发生语法解析错误。
  • TypeError:操作数类型错误。
  • URLError:URL处理函数使用不当。