# ES5

javascript最早是由网景公司推出的,极其简单,被很多开发者接受,逐渐流行起来,后来IE为了抢占市场微软,将IE浏览器内置在windows系统中,所以IE的市场占有率相当的高。IE脚本语言是Jscript(vbscript)

网景公司为了推广js,与sun公司合作,为了让js更流行,借助当时极其流行的语法java,将js更名为javascript,所以java与javascript关系就像雷锋和雷峰塔。网景公司做了一件好事,将js的语言规范提交给ECMA组织,所以我们学习ECMAScript规范就是在学习javascript规范,所以ECMAScript是js规范的未来。微软很有个性,自己非要研制一套规范,研制的非常不好用,后来自己内部工程师都不干了,非要重新研制新的浏览器,所以微软决定放弃xp系统(放弃IE6,7)。重新研制了IE9浏览器,完全遵守ECMAScript语言规范,所以IE9是微软的第一代高级浏览器(是所有高级浏览器中,最差的一款)。

在国内,我们还要维护IE6,7,8,原因是国内一些企业决定维护xp系统,所以IE6,7就无法淘汰,所以就苦了国内的工程师了,还要维护IE6,7,8

好消息是移动端基本都是webkit内核,因此我们可以放心的使用html5,css3,ES5规范等等 在pc端,由于高级浏览器都实现了html5,css3,ES5规范等等,所以我们可以直接用高级浏览器测试它们ES规范版本 ES1, ES2, ES3, ES4, ES3.1, ES5, ES6, ES2016, ES2017, ES2018

# JSON的拓展

# parse


该方法用于将json字符串解析为js对象
    使用方式:
        JSON.parse(str, fn) 
            str: 要处理的字符串
            fn: 要处理的函数
                返回值就是本次处理的结果
                有两个参数
                第一个参数: 属性名
                第二个参数: 属性值
    从外部向内部遍历 

// 定义json字符串
var str = '{"a": 1, "b": "2", "c": {"d": 3}}';

// 将str转为js对象
var result = JSON.parse(str, function(key, value) {
	// 我们想让所有的字符串作为数字保留
	// console.log(arguments);
	// 判断value的类型
	if (typeof value === "string") {
		return +value;
	}
	return value;
});

# stringify

该方法是用于js对象解析为json字符串的
使用方式:
    JSON.stringify(obj, fn)
        obj: 要处理的对象
        fn:  要处理的函数
            返回值就是本次处理的结果
            有两个参数
                第一个参数: 属性名
                第二个参数: 属性值
    从内部向外部遍历
// 定义对象
var obj = {
	a: 1,
	b: "2",
	c: {
		d: 3
	}
}

// 将Obj转为json字符串
var result = JSON.stringify(obj, function(key, value) {
	// 我们想要将字符串作为数字保留
	// console.log(arguments);
	if (typeof value === "string") {
		return +value;
	}
	return value;
});

# 数组的拓展

# 判断数组

第一种方式判断对象的类型
    Object.prototype.toString.call(obj)
第二种方式是否是实例化对象
    obj instanceof Array
第三种方式判断构造函数是否是Array
	obj.constructor === Array
第四种方式数字的静态方法
    Array.isArray(obj)
// instanceof
console.log(arr instanceof Array);
// 构造函数是Object的时候,判断也是true
console.log(arr instanceof Object); 
console.log(obj instanceof Array);


// constructor
console.log(arr.constructor === Array);
console.log(arr.constructor === Object); // false
console.log(obj.constructor === Array);


// toString
console.log(Object.prototype.toString.call(arr) === "[object Array]");
console.log(Object.prototype.toString.call(obj) === "[object Array]");

// 数组的静态方法
console.log(Array.isArray(arr));
console.log(Array.isArray(obj));

# 获取成员的索引

ES5为数组拓展了两个方法:indexOf、 lastIndexOf 分别用于获取成员的索引
    参数就要查找的成员
    如果查找到成员返回该成员的索引
    如果没有找到该成员返回-1
在查找成员的时候不会做类型转换, 是真正的全等查找
// 兼容IE
if (!Array.prototype.indexOf) {
	// 拓展方法
	Array.prototype.indexOf = function(item) {
		// 遍历数组, 就是遍历this
		for (var i = 0; i < this.length; i++) {
			// i 表示索引值, this[i] 表示成员值
			if (this[i] === item) {
				// 找到成员返回索引
				return i;
			}
		}
		// 循环完毕,没有找到 返回-1
		return -1;
	}
}

# lastIndexOf

// 兼容lastIndexOf
if (!Array.prototype.lastIndexOf) {
	// 拓展方法
	Array.prototype.lastIndexOf = function(item) {
		// 遍历数组
		for (var i = this.length - 1; i >= 0; i--) {
			// i 表示索引值, this[i] 表示成员值
			if (this[i] === item) {
				// 找到成员返回索引
				return i;
			}
		}
		// 循环完毕,没有找到 返回-1
		return -1;
	}
}

# forEach

是数组迭代器方法,该方法用于替代for循环, 并没有将for循环移除, 只是将循环封装在了数组迭代器forEach方法的内部
	使用方式:
        数组.forEach(fn)
            fn: 要执行的函数
                有三个参数
                    第一个参数: 成员值
                    第二个参数:  索引值
                    第三个参数: 原数组
                    作用域是window
                    返回值对forEach执行的结果没有影响
        forEach方法的返回值始终undefined
        jQuery中也有一个类似的方法, each,   jquery中的each方法与forEach方法的区别就是
        jquery中的each方法中函数中的第一个参数是索引值,第二个参数是成员值
// 兼容IE
if (!Array.prototype.forEach) {
	// 拓展方法
	Array.prototype.forEach = function(fn) {
		// 要遍历数组, 就是在遍历this
		for (var i = 0; i < this.length; i++) {
			// 执行函数并传递参数
			// 有三个参数: 成员值: this[i]、 索引值: i, 原数组: this
			fn(this[i], i, this);
		}
	}
}

# map

该方法用于遍历数组并映射结果, 与forEach方法类似, 只是map方法的返回值是有意义的
使用方式:
    数组.map(fn)
        fn: 要执行的函数
            有三个参数: 成员值, 索引值, 原数组
            返回值就是执行结果的数组成员
map方法的返回值是一个新的数组, 数组中的成员都是每一次函数执行结果组成的成员
// 兼容IE 
if (!Array.prototype.map) {
	// 拓展方法
	Array.prototype.map = function(fn) {
		// 创建结果容器
		var result = [];
		// 遍历数组
		for (var i = 0; i < this.length; i++) {
			// 执行函数并传递参数
			// 传递三个参数: 成员值 this[i] 、 索引值 i、 原数组 this
			// 将函数的执行结果放入数组容器中
			result.push(fn(this[i], i, this));
		}
		// 返回新的数组
		return result;
	}
}

# fill

    该方法用于填充数组
    通过Array(len) 或者new Array(len) 得到的只有数组的长度没有成员,所以我们不能使用数组迭代器方法(forEach,map)进行遍历, 就要填充数组
    参数就是要填充的成员, 即使是函数也不会执行
// 兼容IE
if (!Array.prototype.fill) {
	// 拓展该方法
	Array.prototype.fill = function(item) {
		// 遍历数组, 就是遍历this
		for (var i = 0; i < this.length; i++) {
			// 填充数组成员
			this[i] = item;
		}
		// 返回填充后的数组
		return this;
	}
}

# 断言方法

# some

判断数组中是否有成员满足条件
使用方式与forEach方法类似
参数就是要执行的函数
    函数中有三个参数: 成员值, 索引值, 原数组
    返回值就是判断的依据
some方法的返回值:
    true: 至少有一个成员满足条件
    false: 都不满足条件
some方法对true敏感, 一旦遇到满足条件的成员就立即停止遍历
/**
 * 实现some方法
 * @arr 要遍历的数组
 * @fn  要执行的函数
 * return bool 是否有成员满足条件
 **/
function some(arr, fn) {
	// 遍历数组
	for (var i = 0; i < arr.length; i++) {
		// 执行函数并判断结果
		// 传递三个参数: 成员值 arr[i]、 索引值 i 、 原数组 arr
		if (fn(arr[i], i, arr)) {
			// 如果执行结果为真, 返回true
			return true;
		}
	}
	// 遍历完成没有找到
	return false;
}

# every

判断数组中是否都满足条件
与forEach方法类似
参数就是要执行的函数
    有三个参数: 成员值、 索引值、 原数组
    返回值就是判断的依据
every方法的返回值:
    true: 都满足条件
    false: 至少有一个成员不满足条件
every方法对false敏感, 一旦遇到一个不满足条件的成员立即停止遍历
/**
 * 实现every方法
 * @arr 要遍历的数组
 * @fn  要执行的函数
 * return bool 是否都满足条件
 **/
function every(arr, fn) {
	// 遍历数组
	for (var i = 0; i < arr.length; i++) {
		// 执行函数并判断结果
		// 传递三个参数: 成员值 arr[i]、 索引值 i 、 原数组 arr
		if (!fn(arr[i], i, arr)) {
			// 遇到不满足条件的停止遍历并返回false
			return false;
		}
	}
	// 都满足条件返回true
	return true;
}

# filter

实现对数组的过滤
使用方式和forEach方法类似
参数就是要执行的函数
    有三个参数: 成员值、索引值、 原数组
    返回值就是过滤的条件
filter方法的返回值就是满足过滤条件的成员组成的新数组
/**
 * 实现filter方法
 * @arr 要遍历的数组
 * @fn  要执行的函数
 * return [] 满足过滤条件的成员组成的新数组
 **/
function filter(arr, fn) {
	// 创建一个结果容器
	var result = [];
	// 遍历数组
	for (var i = 0; i < arr.length; i++) {
		// 执行函数并判断结果
		// 传递三个参数: 成员值 arr[i]、 索引值 i、 原数组 arr
		if (fn(arr[i], i, arr)) {
			// 将满足过滤条件的成员放入结果容器中
			result.push(arr[i]);
		}
	}
	// 返回result
	return result;
}

# reduce

# reduceRight

这两个是累加的方法,reduce是从前向后累加, 而reduceRight是从后向前累加
对所有成员逐一处理,并将结果返回
参数就是要执行的函数
    有四个参数: 上一次的累积结果,当前成员值, 当前索引值, 原数组
    返回值就是当次累积的结果, 将会在下一次遍历的时候作为第一个参数传递
    reduce是从第二个成员开始遍历, 第一个成员会在第一次遍历的时候做为第一个参数传递
    reduceRight是从倒数第二个成员开始遍历,倒数第一个成员在第一次遍历的时候作为第一个参数
/**
 * 实现reduceRight方法
 * @arr 要计算的数组
 * @fn  要执行的函数
 * return 累积结果
 */
 function reduceRight(arr, fn) {
 	// 定义累计结果
 	// 倒数第一个成员是当次累积的结果
 	var result = arr[arr.length - 1];
 	// 遍历数组
 	for (var i = arr.length - 2; i >= 0; i--) {
 		// 执行函数传递参数
 		// 四个参数: 上一次的累计结果, 当前成员值, 当前索引值, 原数组
 		result = fn(result, arr[i], i, arr)
 	}
 	// 返回累积结果
 	return result;
 }

# 实现addNum方法

addNum(num1, num2)  接受两个参数, 分别是两个整数, 求两个整数之间所有整数之和
比如: 
    addNum(1, 100)

    一种是包含两个参数, 两一种是不包含两个参数
    我们统一包含两个参数
// 定义函数
function addNum(num1, num2) {
	// 确定最大值和最小值
	var max = Math.max(num1, num2);
	var min = Math.min(num1, num2);
	// console.log(max);
	// console.log(min);

	// (5, 10)  5, 6, 7, 8, 9, 10   10 - 5 + 1
	// (5, 10)  6, 7, 8, 9			10 - 5 - 1

	// 创建数组
	return Array(max - min + 1)
	// 为了遍历数组,需要填充数组
	.fill(1)
	// 要构建一个从最小值到最大值之间的一个数组
	.map(function(value, index, arr) {
		// index递加, 用min + index 即可得到一个从最小值到最大值之间的数组
		return min + index;
	})
	// 求累加结果
	.reduce(function(pre, value, index, arr) {
		return pre + value;
	})
}

# 函数绑定

ES5对函数拓展了bind方法
    作用:为函数绑定作用域(当函数执行的时候,改变函数的作用域,并传递参数)
    call与apply的区别
        他们都是改变函数作用域的方法,都是在调用该方法的时候,执行函数并改变作用域的,第一个参数都是改变的作用域
    call 从第二个参数开始,表示传递给函数的参数
    apply 第二个参数是数组,每一个成员表示传递给函数的参数 
bind跟call类似
    第一个参数表示改变的作用域对象
    从第二个参数开始,表示传递的参数
区别:
    call | apply 调用即执行
    bind调用不执行,但是得到一个新的方法,可以执行

# 日期拓展

toJSON 将日期转化成json格式,(标准化格式)

var date = new Date();
console.log(date)
console.log(date.toJSON())

# Property getters and setters

ES5 lets you define object methods with a syntax that looks like getting or setting a property.

// Create an object:
var person = {
  firstName: "John",
  lastName : "Doe",
  get fullName() {
    return this.firstName + " " + this.lastName;
  }
};

// Display data from the object using a getter:
document.getElementById("demo").innerHTML = person.fullName;

# Reserved words as property names

var obj = {name: "John", new: "yes"}

# Object.create()

// Create an Object:
const person = {
  firstName: "John",
  lastName: "Doe"
};

// Create new Object
const man = Object.create(person);
man.firstName = "Peter";

# Object.keys()

// Create an Object
const person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

// Get the Keys
const keys = Object.keys(person);

# Object management

// Adding or changing an object property
Object.defineProperty(object, property, descriptor)

// Adding or changing object properties
Object.defineProperties(object, descriptors)

// Accessing a Property
Object.getOwnPropertyDescriptor(object, property)

// Accessing Properties
Object.getOwnPropertyDescriptors(object)

// Returns all properties as an array
Object.getOwnPropertyNames(object)

// Accessing the prototype
Object.getPrototypeOf(object)

# Object protection

// Prevents adding properties to an object
Object.preventExtensions(object)

// Returns true if properties can be added to an object
Object.isExtensible(object)

// Prevents changes of object properties (not values)
Object.seal(object)

// Returns true if object is sealed
Object.isSealed(object)

// Prevents any changes to an object
Object.freeze(object)

// Returns true if object is frozen
Object.isFrozen(object)

# Object defineProperty()

// Create an Object:
const person = {
  firstName: "John",
  lastName : "Doe",
  language : "NO",
};

// Change a Property:
Object.defineProperty(person, "language", {
  value: "EN",
  writable : true,
  enumerable : true,
  configurable : true
});

// Enumerate Properties
let txt = "";
for (let x in person) {
  txt += person[x] + "<br>";
}

// Display Properties
document.getElementById("demo").innerHTML = txt;

# Function bind()

const person = {
  firstName:"John",
  lastName: "Doe",
  fullName: function () {
    return this.firstName + " " + this.lastName;
  }
}

const member = {
  firstName:"Hege",
  lastName: "Nilsen",
}

let fullName = person.fullName.bind(member);

# Trailing commas