一、JavaScript概述
1、ECMAScript和JavaScript的关系
1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组织ECMA,希望这门语言能够成为国际标准。 次年,ECMA发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为ECMAScript,这个版本就是1.0版。 该标准一开始就是针对JavaScript语言制定的,但是没有称其为JavaScript,有两个方面的原因。一是商标,JavaScript本身已被Netscape注册为商标。 二是想体现这门语言的制定者是ECMA,而不是Netscape,这样有利于保证这门语言的开发性和中立性。 因此ECMAScript是JavaScript的规格,JavaScript是ECMAScript的一种实现,在日常场合,这两个词是可以互换的。 javascript遵守ECMA指定的标准,换句话说javascript就是ECMAscript的方言。
2、ECMAScript的历史
年份 | 名称 | 描述 |
1997 | ECMAScript 1 | 第一个版本 |
1998 | ECMAScript 2 | 版本变更 |
1999 | ECMAScript 3 | 添加正则表达式 添加try/catch |
| ECMAScript 4 | 没有发布 |
2009 | ECMAScript 5 | 添加"strict mode"严格模式 添加JSON支持 |
2011 | ECMAScript 5.1 | 版本变更 |
2015 | ECMAScript 6 | 添加类和模块 |
2016 | ECMAScript 7 | 增加指数运算符(**) 增加Array.prototype.includes |
3、JavaScript的组成和特点
平常所说的ES6就是指ECMAScript 6
一个完整的JavaScript 实现是由以下 3 个不同部分组成的:
- 核心(ECMAScript)
- 文档对象模型(DOM) Document object model (整合js,css,html)
- 浏览器对象模型(BOM) Broswer object model(整合js和浏览器)
JavaScript的特点
- JavaScript 是脚本语言
- JavaScript 是一种轻量级的编程语言。
- JavaScript 是可插入 HTML 页面的编程代码。
- JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行。
二、JavaScript引入方式
1、行内JS
<input type="button" value="点击我" onclick="javascript:alert('我被点击了')">
<!--onclick:点击触发一个事件,alert:弹出一个对话框-->
2、内部JS:script里的程序整个页面都可以用
<head>
<meta charset="UTF-8">
<title>JS内部定义</title>
<script type="text/javascript">
function Example(){ /*函数*/
alert('我喜欢你')
}
</script>
</head>
3、外部JS
外部样式就是将JS写在一个单独的文件(.js文件)中,然后在HTML页面进行引入。
<script type="text/javascript" src="MyJavascript.js"></script>
三、JavaScript语言规范
1、注释
单行注释://
2、多行注释:/*注释内容*/
/*
多行注释
*/
3、对比
HTML注释:<!--注释内容-->
CSS注释:/*注释内容*/ 单行多行都是用这个
JavaScript注释:
单行注释://
多行注释:/*注释内容*/
4、结束符
JavaScript中的语句要以分号 ; 为结束符。
四、JavaScript语言基础
1、变量声明
JavaScript的变量名可以使用数字,字母,_和$组成,不能以数字开头。
声明变量使用var关键字
var name = "mingzai";
var age = 18;
2、注意事项
1.变量名是区分大小写的
2.推荐使用驼峰式命名规则(python中则推荐使用下划线的形式命名)
3.保留字(关键字)不能用做变量名
4.javascript和python一样是动态的语言,即定义的变量不需要声明是什么类型,例如:
var a = 'xiaoming' //定义一个字符串变量
a = 18 //可以直接修改成数字类型的变量
3、补充
1.ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
例如:
{
let a = 100;
var b = 50;
}
a // Uncaught ReferenceError: a is not defined
b // 50
for循环的计数器,就很合适使用let命令,例如:
for (let i = 0; i < 10; i++) {
代码
}
2.ES6新增const用来声明常量。一旦声明,其值就不能改变。
const ID = 1
ID //1
ID = 2 //VM602:1 Uncaught TypeError: Assignment to constant variable.
abstract
boolean
byte
char
class
const
debugger
double
enum
export
extends
final
float
goto
implements
import
int
interface
long
native
package
private
protected
public
short
static
super
synchronized
throws
transient
volatile
保留字列表
五、JavaScript数据类型
1、介绍
1、JavaScript动态类型
var x; // 此时x是undefined
var x = 1; // 此时x是数字
var x = "xiaoming" // 此时x是字符串
2、数值(Number)
JavaScript不区分整型和浮点型,就只有一种数字类型。
var a = 100 //100
var b = 10.5 //10.5
var c = 12.13e3 //12130
还有一种NaN,表示不是一个数字(Not a Number)
就是说,如果你想把一个字符串转换成数字类型,那么这个字符串必须是全数字组成的才能转换,否则就不能转,
比如字符串'abc' 是不可以转成数字类型的,但是字符串'123'却可以转成数字类型。
常用方法:
parseInt(123) //123
parseInt('123') //123
parseFloat(123) //123
parseFloat('12.3') //12.3
parseInt('abc') //NaN
parseFloat('abc') //NaN
3、字符串(String)
var a = "Hello"
var b = "world;
var c = a + b;
console.log(c); // Helloworld
2、字符串常用方法
方法 | 说明 |
.length | 返回长度 |
.trim() | 移除空白 |
.trimLeft() | 移除左边的空白 |
.trimRight() | 移除右边的空白 |
.charAt(n) | 返回第n个字符 |
.concat(value, ...) | 拼接 |
.indexOf(substring, start) | 子序列位置 |
.substring(from, to) | 切片 |
.slice(start, end) | 切片 |
.toLowerCase() | 小写 |
.toUpperCase() | 大写 |
.split(delimiter, limit) | 分割 |
例子
// 长度
var s1 = 'hello world';
s1.length; //11(length是属性,不是方法)
// 去除空白
var s2 = ' hello ';
s2; //" hello "
s2.trim(); //"hello"(相当于python的strip())
s2.trimLeft(); //"hello "(相当于python的lstrip())
s2.trimRight(); //" hello"(相当于python的rstrip())
// 返回第n个字符(根据索引取值)
var s3 = 'hello girl';
s3.charAt(0); //"h" 相当于python的按索引找值
s3.charAt(11); //"" 没有值则返回空字符串
s3[0] // "h" (与 charAt 效果相同,但是不存在的值返回undefined)
// 拼接
var s4 = 'hello';
s4.concat('boy','girl'); //"helloboygirl"
// 找元素的索引位置,跟python的index相似,但有不同
// indexOf接收两个参数,第一个参数是要找的元素,第二个参数是表示从哪里开始找,找不到就返回-1
var s4 = 'hello';
s4.indexOf('e'); //1 默认从索引为0开始找,找到元素e,e的索引是1
s4.indexOf('e',1); //1 从索引为1开始找,找到元素e,e的索引是1
s4.indexOf('e',2); //-1 从索引为2开始找,找不到元素e,返回-1
// 切片的两种方式
substring:不支持负数,因为若第一个参数大于第二个参数,则会把两个参数互换,若参数是负数会把这个负数变成0
例如:substring(1,-5) --> substring(-5,1) --> substring(0,1)
slice:跟我们python中的切片一样,也支持负数
var s5 = 'hello world';
s5.substring(0,5); //"hello"
s5.substring(0,-5); //"" 从0到0
s5.substring(1,-5); //"h" 从0到1
s5.slice(1,-5); //"ello "
//大小写
var s6 = 'Hello World';
s6.toLowerCase(); //"hello world"
s6.toUpperCase(); //"HELLO WORLD"
//分割
var s7 = 'hello world';
s7.split(); //["hello world"] 没有默认参数,不写参数不分割,但是还是生成了列表
s7.split(' '); //["hello", "world"] 按照空格分割
s7.split(' ',1); //["hello"] 第二个参数是代表返回的列表的长度
s7.split(' ',2); //["hello", "world"]
s7.split(' ',3); //["hello", "world"] 超出列表的长度,也相当于把整个列表的长度都返回
//拼接字符串一般使用“+”
var name = 'xiaoming';
var age = 18;
msg = name + age; //"xiaoming18"
注意:slice和substring的区别
string.slice(start, stop)
string.substring(start, stop):
两者的相同点:
如果start等于end,返回空字符串
如果stop参数省略,则取到字符串末
如果某个参数超过string的长度,这个参数会被替换为string的长度
substirng()的特点:
如果 start > stop ,start和stop将被交换
如果参数是负数或者不是数字,将会被0替换
silce()的特点:
如果 start > stop 不会交换两者
如果start小于0,则切割从字符串末尾往前数的第abs(start)个的字符开始(包括该位置的字符)
如果stop小于0,则切割在从字符串末尾往前数的第abs(stop)个字符结束(不包含该位置字符)
模板字符串
ES6中引入了模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识(就是ESC下面那个键),用${}取值
它可以当做普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
如果模板字符串中需要使用反引号,则在其前面要用反斜杠转义。
JavaScript模板字符串
var name = '明哥';
var msg = `I am ${name}`; //"I am 明哥"
var msg2 = `I am ${name}
I am 18 year old`;
结果:
`I am 明哥
I am 18 year old`;
//直接定义多行字符串
var msg3 = 'I am ${name}
I am 18 year old';
结果:Uncaught SyntaxError: Invalid or unexpected token
python模板字符串
f'xxx' 或 F'xxx' 且以大括号 {} 表明被替换的字段,{}里面可以是字符串或者表达式
name = '小白'
msg = f'我叫{name},今年{18}岁,性别{"男"}'
print(msg) # 我叫小白,今年18岁,性别男
3、布尔值(Boolean)
区别于Python,JavaStript的true和false都是小写。
var a = true;
var b = false;
""(空字符串)、0、null、undefined、NaN都是false。
4、null和undefined
null表示有值的,但是值是空,一般在需要指定或清空一个变量时才会使用,如 name=null;
undefined表示当声明一个变量但未初始化时,该变量的默认值是undefined。还有就是函数无明确的返回值时,返回的也是undefined。
var name //此时声明了变量,但未初始化,默认值是undefined
5、数组
数组对象的作用是:使用单独的变量名来存储一系列的值。类似于Python中的列表。
var a = [1234, "heiheihei"];
console.log(a[1]); // "heiheihei"
数组的常用方法
方法 | 说明 |
.length | 数组的大小 |
.push(ele) | 在尾部追加元素 |
.pop() | 删除尾部的元素并返回删除的元素 |
.unshift(ele) | 在头部插入元素 |
.shift() | 在头部移除元素 |
.slice(start, end) | 切片 |
.splice(index,howmany,item1,.....,itemX) | 向数组中添加/删除项目,然后返回被删除的项目 |
.reverse() | 反转 |
.join(seq) | 将数组元素连接成字符串 |
.concat(val, ...) | 连接数组,生成新的数组 |
.sort() | 排序 |
.forEach() | 将数组的每个元素传递给回调函数 |
.map() | 返回一个数组元素调用函数处理后的值的新数组 |
.filter() | filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素 |
例子
//索引
var a1 = [1,2,3,4];
a1[1]; // 2
a1[6]; // undefined 超出范围就返回undefined
//数组的大小
a1.length; //4
//增加删除元素
var a2 = [1,2,3];
//在尾部追加
a2.push(4); //[1, 2, 3, 4]
//在尾部删除
a2.pop(); // [1, 2, 3]
//在头部增加
a2.unshift(0); //[0, 1, 2, 3]
//在头部删除
a2.shift(); //[1, 2, 3]
//切片
var a3 = [1,2,3,4,5];
a3.slice(1,3); //[2, 3]
a3.slice(1,-1); //[2, 3, 4]
//把数组反过来
a3.reverse(); //[5, 4, 3, 2, 1]
//将数组元素连接成字符串
a3.join('+') //"5+4+3+2+1"
//Python中的join
list1 = [11, 22, 33, 44]
ret = '+'.join([str(i) for i in list1])
//连接数组(形成新的数组)
var a4 = [1,2,3,4];
new_a4 = a4.concat(5,6,7); //[1, 2, 3, 4, 5, 6, 7]
new_a4_2 = a4.concat(['a','b','c']); //[1, 2, 3, 4, "a", "b", "c"]
//关于sort()需要注意:
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort(); // 对 fruits 中的元素进行排序
fruits.reverse(); // 反转元素顺序
默认地,sort() 函数按照 字符串顺序 进行排序。
该函数很适合字符串("Apple" 会排在 "Banana" 之前)。
不过,如果数字按照字符串来排序,则 "25" 大于 "100",因为 "2" 大于 "1"。(1,2,3,4顺序)
正因如此,sort() 方法在对数值排序时会产生不正确的结果。
对于数值排序,我们可以通过一个 比值函数 来修正此问题:
// 升序
var points = [40, 100, 1, 5, 25, 10];
points.sort(function(a, b){return a - b});
// 同样,降序的例子
var points = [40, 100, 1, 5, 25, 10];
points.sort(function(a, b){return b - a});
比值函数:比较函数的目的是定义另一种排序顺序。
比较函数应该返回一个负,零或正值,这取决于参数:
function(a, b){return a-b}
当 sort() 函数比较两个值时,会将值发送到比较函数,并根据所返回的值(负、零或正值)对这些值进行排序。
当比较 40 和 100 时,sort() 方法会调用比较函数 function(40,100)。
结果大于0,则交换两个值的位置。
//数组的删除splice接收三个参数:
第一个参数:必需,添加/删除项目的位置,使用负数可从数组结尾处规定位置
第二个参数:必需,要删除的项目数量。如果设置为 0,则不会删除项目。
第三个参数:可选。向数组添加的新项目,不写则不添加
// 清空数组
var arr = [1,2,3,4]
arr.splice(0,arr.length)
// 向数组添加新元素
var arr = [1,2,3,4]
arr.splice(1, 0, 5) // 在index=1的位置删除0个元素,并在此位置添加1个元素5。结果:[1, 5, 2, 3, 4]
// 删除某个元素后,再添加新元素
var arr = [1,2,3,4]
arr.splice(1, 2, 6) // 在index=1的位置删除2个元素,并在此位置添加1个元素6。结果:[1, 6, 4]
var arr = [1,2,3,4]
arr.splice(1, 2, 7, 8) // 在index=1的位置删除2个元素,并在此位置添加2个元素7、8。结果:[1, 7, 8, 4]
补充:
ES6新引入了一种新的原始数据类型(Symbol),表示独一无二的值。它是JavaScript语言的第7种数据类型。
两个数组合并
// 方法一
时常看到在操作数组的时候有这样的写法:
var a = [1,2,3];
var b = [4,5,6];
a.push.apply(a, b); // 向a数组push一个b数组
console.log(a) // [1,2,3,4,5,6]
// 方法二
上面写法等价于:
var a = [1,2,3];
var b = [4,5,6];
Array.prototype.push.apply(a, b); // 向a数组push一个b数组
console.log(a) // [1,2,3,4,5,6]
这样写法等价的原因是因为在实例上寻找属性的时候,现在这个实例自己身上找,如果找不到,就根据内部指针__proto__随着原型链往上找,直到找到这个属性。
在这里就是寻找push方法,两种写法最后找到的都是Array构造函数对应的prototype的原生方法push。所以说两种写法是等价的。
但是为什么要使用a.push.apply(a,b);这种写法呢?为什么不直接使用push()?
如果直接push:
var a = [1,2,3];
var b = [4,5,6];
a.push(b);
console.log(a) // [1, 2, 3, Array(3)]
因为原生push方法接受的参数是一个列表时,它不会自动把数组扩展成一个个参数,
使用apply的写法可以将数组型参数扩展成参数列表,这样合并两个数组就可以直接传数组参数了。
但是合并数组为什么不直接使用Array.prototype.concat()呢?
因为concat不会改变原数组,concat会返回新数组,而上面apply这种写法直接改变数组a。
基于速度和内存的想法,用上面的方法会好点,但是你非要用concat也行。
var a = [1,2,3];
var b = [4,5,6];
var res = a.concat(b);
console.log(a) // [1, 2, 3]
console.log(res) // [1,2,3,4,5,6]
foreEach()方法
forEach()和map()两个方法都是ECMA5中Array引进的新方法,主要作用是对数组的每个元素执行一次提供的函数,但是它们之间还是有区别的。jQuery也有一个方法$.each(),长得和forEach()有点像,功能也类似。但是从本质上还是有很大的区别的。
针对每一个元素执行提供的函数。
语法:
array.forEach(callback(currentValue, index, array){
//do something
}, this)
//或者
array.forEach(callback(currentValue, index, array){
//do something
})
// 注意:index和array是可选参数,参数一:当前数组中元素;参数二:索引; 参数三:当前数组。
map()方法
创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来。
语法:
var new_array = array.map(callback(currentValue, index, array){
//do something
})
// 注意:index和array是可选参数,参数一:当前数组中元素;参数二:索引; 参数三:当前数组。
区别
forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。
而map()方法会得到一个新的数组并返回。
执行速度对比
forEach()的执行速度 < map()的执行速度
例如:
var arr =[1,2,3,4,5,6]
// forEach()
我们在回调函数中直接修改arr的值。
arr.forEach(function(value, index){
arr[index] = value * value;
});
console.log(arr) // [1, 4, 9, 16, 25, 36]
// Map()
var lst = arr.map(function(value){
return value * 2;
});
console.log(lst) // [2, 8, 18, 32, 50, 72]
each
方式一:循环可迭代对象
jQuery.each(collection, callback(indexInArray, valueOfElement))
jQuery.each(可迭代对象, 函数(索引, 值)) -->类似于python内置函数sorted那种类型
描述:一个通用的迭代函数,它可以用来无缝迭代对象和数组。
数组和类似数组的对象通过一个长度属性(如一个函数的参数对象)来迭代数字索引,从0到length - 1。其他对象通过其属性名进行迭代。
例如:
li =["a","b","c","d"]
$.each(li,function(i, v){
console.log(i, v);//i是索引,v是每次循环的具体元素。
})
输出:
0"a"
1"b"
2"c"
3"d"
li =["a","b","c","d"]
$.each(li,function(i, v){
if (i === 2){
return false; // 相当于break
}
console.log(i, v);
})
输出:
0"a"
1"b"
li =["a","b","c","d"]
$.each(li,function(i, v){
if (i === 2){
return; // 相当于continue
}
console.log(i, v);
})
输出:
0"a"
1"b"
3"d"
d = {"name": "ming", "age": 18}
$.each(d,function(k,v){
console.log(k,v) // k是对象的键,v是对象的值
})
"name" "ming"
"age" 18
方式二:循环节点
somenode.each(function(index, Element))
描述:遍历一个jQuery对象,为每个匹配元素执行一个函数。
.each() 方法用来迭代jQuery对象中的每一个DOM元素。每次回调函数执行时,会传递当前循环次数作为参数(从0开始计数)。
由于回调函数是在当前DOM元素为上下文的语境中触发的,所以关键字 this 总是指向这个元素。
例如:
// 为每一个li标签添加一个样式
$("li").each(function(){
$(this).addClass("c1");
});
注意:
jQuery的方法返回一个jQuery对象,遍历jQuery集合中的元素 - 被称为隐式迭代的过程。当这种情况发生时,它通常不需要显式地循环的 .each()方法
也就是说,上面的例子没有必要使用each()方法,直接像下面这样写就可以了:
$("li").addClass("c1"); // 对所有标签做统一操作
filter
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
注意: filter() 不会对空数组进行检测。
注意: filter() 不会改变原始数组。
语法: array.filter(function(currentValue, index, arr){}, thisValue)
参数说明
参数 | 描述 |
function(currentValue, index,arr) | 必须。函数,数组中的每个元素都会执行这个函数 函数参数: |
thisValue | 可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。 如果省略了 thisValue ,"this" 的值为 "undefined" |
参数 | 描述 |
currentValue | 必须。当前元素的值 |
index | 可选。当前元素的索引值 |
arr | 可选。当前元素属于的数组对象 |
// 利用 filter 遍历出所有偶数
let arr = [56, 15, 48, 3, 7];
let newArr = arr.filter(function (value, index, array) {
return value % 2 === 0;
});
console.log(newArr) // [56, 48]
// 利用 filter 进行数组去重
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
var arr = [1, 1, 'RUNOOB', 'RUNOOB', true, true, 15];
console.log(unique(arr)) // [1, "RUNOOB", true, 15]
6、Object对象
JS中的对象,类似于python中的字典,key可以不加引号,key默认是字符串
JavaScript的对象(Object)本质上是键值对的集合(Hash结构),但是只能用字符串作为键。
1.普通创建
var people = {"name": "xiaoming", "age": 18};
console.log(people.name);
console.log(people["age"]);
遍历对象中的内容:
var person = {"name": "xiaohua", "age": 18};
for (var key in person){
console.log(key); // 对象的key
console.log(person[key]);
}
2.使用new创建对象
var person=new Object(); // 创建一个person对象
person.name="xiaoming"; // person对象的name属性
person["age"]=18; // person对象的age属性
person; // {name: "xiaoming", age: 18}
3.Object.keys: 遍历可枚举的属性,只包含对象本身可枚举属性,不包含原型链可枚举属性
let arr = ["a", "b", "c"];
let obj = { foo: "bar", baz: 42 };
let ArrayLike = { 0 : "a", 1 : "b", 2 : "c"};
Object.keys(arr) // ['0', '1', '2']
Object.keys(obj) // ["foo","baz"]
Object.keys(ArrayLike) // ['0', '1', '2']
4.Object.values: 遍历可枚举的属性值,只包含对象本身可枚举属性值,不包含原型链可枚举属性值
let arr = ["a", "b", "c"];
let obj = { foo: "bar", baz: 42 };
let ArrayLike = { 0 : "a", 1 : "b", 2 : "c"};
Object.values(arr) // ["a", "b", "c"]
Object.values(obj) // ["bar",42]
Object.values(ArrayLike) // ["a", "b", "c"]
6.1、Object对象静态方法
转载于:https://www.jianshu.com/p/c5aa1eee8dfd
删除对象中的某个属性
delete
是删除对象属性唯一真正的方法
var obj = {
"name": "zzz",
"age": 18
};
delete obj.age;
console.log(obj);
create 创建一个对象
const obj = Object.create({a:1}, {b: {value: 2}})
第一个参数为对象,对象为函数调用之后返回新对象的原型对象,第二个参数为对象本身的实例方法(默认不能修改,不能枚举)
obj.__proto__.a === 1 // true
obj.b = 3;
console.log(obj.b) // 2
//创建一个可写的,可枚举的,可配置的属性p
obj2 = Object.create({}, {
p: {
value: 2, // 属性值
writable: true, // 是否可以重写值
enumerable: true, //是否可枚举
configurable: true //是否可以修改以上几项配置
}
});
obj2.p = 3;
console.log(obj2.p) // 3
注意: enumerable 会影响以下
for…in 遍历包括对象原型上属性
Object.keys() 只能遍历自身属性
JSON.stringify 只能序列化自身属性
defineProperty Object.defineProperty(object, prop, descriptor)定义对象属性
添加数据属性
var obj = {};
// 1.添加一个数据属性
Object.defineProperty(obj, "newDataProperty", {
value: 101,
writable: true,
enumerable: true,
configurable: true
});
obj.newDataProperty // 101
// 2.修改数据属性
Object.defineProperty(obj, "newDataProperty", {
writable:false
});
//添加访问器属性
var obj = {};
Object.defineProperty(obj, "newAccessorProperty", {
set: function (x) {
this.otherProperty = x;
},
get: function () {
return this.otherProperty;
},
enumerable: true,
configurable: true
});
注意: 1.第一个参数必须为对象
2.descriptor 不能同时具有 (value 或 writable 特性)(get 或 set 特性)。
3.configurable 为false 时,不能重新修改装饰器
defineProperties Object.defineProperties(object, {prop1 : descriptor1, prop2 : descriptor2, ...)
var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
keys 遍历可枚举的属性,只包含对象本身可枚举属性,不包含原型链可枚举属性
let arr = ["a", "b", "c"];
let obj = { foo: "bar", baz: 42 };
let ArrayLike = { 0 : "a", 1 : "b", 2 : "c"};
Object.keys(arr) // ['0', '1', '2']
Object.keys(obj) // ["foo","baz"]
Object.keys(ArrayLike) // ['0', '1', '2']
values 遍历可枚举的属性值,只包含对象本身可枚举属性值,不包含原型链可枚举属性值
let arr = ["a", "b", "c"];
let obj = { foo: "bar", baz: 42 };
let ArrayLike = { 0 : "a", 1 : "b", 2 : "c"};
Object.values(arr) // ["a", "b", "c"]
Object.values(obj) // ["bar",42]
Object.values(ArrayLike) // ["a", "b", "c"]
assign Object.assign( target, source, source1 ) 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
特殊情况:
let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]
Object.assign方法实行的是浅拷贝,而不是深拷贝。
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
console.log(obj2.a.b) //2
obj2.a.b = 3
console.log(obj1.a.b) //3
getPrototypeOf 获取指定对象的原型(内部[[Prototype]]属性的值)
const prototype1 = {};
const object1 = Object.create(prototype1);
console.log(Object.getPrototypeOf(object1) === prototype1); // true
注意:Object.getPrototypeOf(Object) 不是 Object.prototype
Object.getPrototypeOf( Object ) === Function.prototype; // true
setPrototypeOf 设置一个指定的对象的原型
const obj = {a: 1}, proto = {b:2}
Object.setPrototypeOf(obj, proto)
obj.__proto__ === proto //true
getOwnPropertyNames 与keys相似,但包含遍历包含不可枚举属性
var my_obj = Object.create({}, {
getFoo: {
value: function() { return this.foo; },
enumerable: false
}
});
my_obj.foo = 1;
Object.getOwnPropertyNames(my_obj).sort() // ["foo", "getFoo"]
getOwnPropertyDescriptor 获取该属性的描述对象
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// { value: 123, writable: true, enumerable: true, configurable: true }
getOwnPropertyDescriptors 返回指定对象所有自身属性(非继承属性)的描述对象
Object.getOwnPropertyDescriptors 方法,返回指定对象所有自身属性(非继承属性)的描述对象。
const obj = {
foo: 123,
get bar() { return 'abc' }
};
console.dir(Object.getOwnPropertyDescriptors(obj))
// { foo:{ value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:{ get: [Function: bar],
// set: undefined,
// enumerable: true,
// configurable: true }
// }
使用场景:
Object.assign() 方法只能拷贝源对象的可枚举的自身属性,同时拷贝时无法拷贝属性的特性,而且访问器属性会被转换成数据属性,也无法拷贝源对象的原型
Object.create() 方法可以实现上面说的这些,配合getPrototypeOf,以及getOwnPropertyDescriptors实现全面浅拷贝
Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
entries 分割对象
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
// string
Object.entries('abc') // [['0', 'a'], ['1', 'b'], ['2', 'c']]
Object.entries(100) // []
is 它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致
Object.is('foo', 'foo') // true
Object.is({}, {}) // false
不同于 === 之处
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
preventExtensions 让一个对象变的不可扩展,也就是永远不能再添加新的属性&isExtensible 判断一个对象是否可扩展
let empty = {}
Object.isExtensible(empty) //true
empty.a = 1 //添加成功
//将对象变为不可拓展
Object.preventExtensions(empty)
Object.isExtensible(empty) //false
empty.b = 2 //静默失败,不抛出错误
empty.a = 5 //修改a属性值为5 修改成功
总结:
1.preventExtensions 可以让这个对象变的不可扩展,也就是不能再有新的属性。
2.需要注意的是不可扩展的对象的属性通常仍然可以被删除。
3.尝试给一个不可扩展对象添加新属性的操作将会失败,不过可能是静默失败,也可能会抛出 TypeError 异常(严格模式)。
4.Object.preventExtensions 只能阻止一个对象不能再添加新的自身属性,仍然可以为该对象的原型添加属性。
seal将一个对象密封 isSealed 判断一个对象是否为密封的
密封对象是指那些不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性,但可能可以修改已有属性的值的对象。
1. 先讲seal 方法:
var o2 = {b: 1}
o2.d = 2 //添加成功
var obj2 = Object.seal(o2);
obj2 === o2 //true 方法返回原对象,栈指针指向同一块内存
Object.isSealed(o2) // true
o2.b = 111 //修改b属性值成功
o2.f = 222 //静默失败,属性f没有成功添加
delete o2.b //静默失败,属性b没有成功删除
2. 讲isSealed 方法:
let o = {};
Object.isSealed(o) //false
// 之后通过Object.preventExtensions方法将空对象设置为不可扩展。
Object.preventExtensions(o);
Object.isSealed(o) // true
但是如果为非空对象呢?
let o2 = {a: 1}
Object.preventExtensions(o2);
Object.isSealed(o2) // false
因为属性 a 是可配置的(configurable为true),所以不是密封的对象,修改方法如下:
let o2 = {a: 1}
Object.preventExtensions(o2);
Object.defineProperty(o2, "a", { configurable: false });
Object.isSealed(o2) //true
总结: 1.密封一个对象会让这个对象变的不能添加新属性,且所有已有属性会变的不可配置。
2.属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。
3.但属性的值仍然可以修改。
4.尝试删除一个密封对象的属性或者将某个密封对象的属性从数据属性转换成访问器属性,结果会静默失败或抛出TypeError 异常(严格模式)。
freeze 冻结一个对象&isFrozen 判断一个对象是否已经被冻结
冻结对象是指那些不能添加新的属性,不能修改已有属性的值,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性的对象。也就是说,这个对象永远是不可变的。
1.先讲freeze 方法:
let o3 = {a: 1}
o3.b = 2 //添加属性b成功
Object.freeze(o3)
Object.isFrozen(o3) //true 对象已被冻结
o3.a = 2 //修改属性a值失败
o3.c = 5 //添加属性c失败
delete o3.b //删除属性b失败
2.再讲isfrozen 方法:
let o4 = {a: 1}
o4.b = 2 // 添加属性b成功
Object.priventExtensions(o4)
Object.defineProperties(o4, {
a: {configurable: false, writable: false},
b: {configurable: false, writable: false}
})
Object.isFrozen(o4) //true o4 已经被冻结
总结:
1.冻结对象的所有自身属性都不可能以任何方式被修改。
2.任何尝试修改该对象的操作都会失败,可能是静默失败,也可能会抛出异常(严格模式中)。
3.数据属性的值不可更改,访问器属性(有getter和setter)也同样(但由于是函数调用,给人的错觉是还是可以修改这个属性)。
4.如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。
浅冻结与深冻结:
(function () {
obj = {
internal :{}
};
Object.freeze(obj);//浅冻结
obj.internal.a = "aValue";
console.log(obj.internal.a);//"aValue"
//想让一个对象变得完全冻结,冻结所有对象中的对象,可以使用下面的函数.
function deepFreeze(o){
var prop,propKey;
Object.freeze(o);//首先冻结第一层对象
for(propKey in o){
prop = o[propKey];
if(!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)){
continue;
}
deepFreeze(prop);//递归
}
}
deepFreeze(obj);
obj.internal.b = "bValue";//静默失败
console.log(obj.internal.b);//undefined
})();
hasOwnProperty
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性
let o = {a: 1 }
o.hasOwnProperty('a') //true
o.hasOwnProperty('b') //false 对象自身没有属性b
o.hasOwnProperty('toString'); //false 不能检测对象原型链上的属性
如何遍历一个对象的所有自身属性,例子:
var buz = {
fog: 'stack'
};
for (var name in buz) {
if (buz.hasOwnProperty(name)) {
console.log("this is fog (" + name + ") for sure. Value: " + buz[name]);
}
else {
console.log(name); // toString or something else
}
}
isPrototypeOf
isPrototypeOf方法用于测试一个对象是否存在于另一个对象的原型链上
function Foo() {}
function Bar() {}
function Baz() {}
Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);
var baz = new Baz();
console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true
propertyIsEnumerable指定的属性是否可枚举
obj.propertyIsEnumerable(prop) prop为被测试的属性名
1. 一般情况下
var o = {};
var a = [];
o.prop = 'is enumerable';
a[0] = 'is enumerable';
o.propertyIsEnumerable('prop'); // 返回 true
a.propertyIsEnumerable(0); // 返回 true
2. 浏览器内置对象
var a = ['is enumerable'];
a.propertyIsEnumerable(0); // 返回 true
a.propertyIsEnumerable('length'); // 返回 false
Math.propertyIsEnumerable('random'); // 返回 false
this.propertyIsEnumerable('Math'); // 返回 false
3. 自身属性和继承属性
(原型链上propertyIsEnumerable不被考虑)
var fn = function(){
this.prop = '123';
}
fn.prototype = { prototypeProp: true}
var o = new fn()
o.propertyIsEnumerable('prop') // true
o.propertyIsEnumerable('prototypeProp') // false
caller 返回当前函数的调用者
function test(){
if(test.caller == null){
alert("JavaScript顶层作用域调用了test()函数");
}else{
alert( test.caller + "函数调用了test()函数");
}
};
test(); // JavaScript顶层作用域调用了test()函数
function callTest(){
test();
}
callTest(); // function callTest(){ test(); }函数调用了test()函数
function callTest2(){
// setTimeout()或setInterval()中定时执行的函数也属于顶层作用域调用
setTimeout(test, 5000); // JavaScript顶层作用域调用了test()函数
}
callTest2();
valueOf 需要返回对象的原始值
备注:js对象中的valueOf()方法和toString()方法非常类似,但是,当需要返回对象的原始值而非字符串的时候才调用它,尤其是转换为数字的时候。如果在需要使用原始值的上下文中使用了对象,JavaScript就会自动调用valueOf()方法。
const o = {a: 1, valueOf: function(){ return 123123 } }
Number(o) //123123
// 给大家出一个题
const o2 = {
x: 1,
valueOf: function(){
return this.x++
}
}
if(o2 == 1 && o2 == 2 && o2 == 3){
console.log('down')
console.log(o2.x)
}else{
console.log('faild')
}
// Array:返回数组对象本身
var array = ["CodePlayer", true, 12, -5];
array.valueOf() === array; // true
// Date:当前时间距1970年1月1日午夜的毫秒数
var date = new Date(2013, 7, 18, 23, 11, 59, 230);
date.valueOf() // 1376838719230
// Number:返回数字值
var num = 15.26540;
num.valueOf() // 15.2654
// 布尔:返回布尔值true或false
var bool = true;
bool.valueOf() === bool // true
// new一个Boolean对象
var newBool = new Boolean(true);
// valueOf()返回的是true,两者的值相等
newBool.valueOf() == newBool // true
// 但是不全等,两者类型不相等,前者是boolean类型,后者是object类型
newBool.valueOf() === newBool // false
// Function:返回函数本身
function foo(){
}
foo.valueOf() === foo // true
var foo2 = new Function("x", "y", "return x + y;");
foo2.valueOf() === foo2 // true
// Object:返回对象本身
var obj = {name: "张三", age: 18};
obj.valueOf() === obj // true
// String:返回字符串值
var str = "http://www.365mini.com";
str.valueOf() === str // true
// new一个字符串对象
var str2 = new String("http://www.365mini.com");
// 两者的值相等,但不全等,因为类型不同,前者为string类型,后者为object类型
str2.valueOf() === str2 // false
getOwnPropertySymbols在给定对象自身上找到的所有 Symbol 属性的数组。
var obj = {};
var a = Symbol("a");
var b = Symbol.for("b");
obj[a] = "localSymbol";
obj[b] = "globalSymbol";
var objectSymbols = Object.getOwnPropertySymbols(obj);
console.log(objectSymbols.length); // 2
console.log(objectSymbols) // [Symbol(a), Symbol(b)]
console.log(objectSymbols[0]) // Symbol(a)
toString toLocalString
toString 方法不做过多介绍
区别:
当被转化的值是个时间对象时,toLocaleString会将转化的结果以本地表示。
(new Date).toString(); //"Mon Nov 06 2017 13:02:46 GMT+0800 (China Standard Time)"
(new Date).toLocaleString(); //"2017/11/6 下午1:03:12"
另外当被转化的值是个时间戳时,toLocaleString会把时间戳每三位添加一个逗号,代码如下。
(Date.parse(new Date())).toLocaleString() //"1,509,944,637,000"
(Date.parse(new Date())).toString() //"1509944643000"
length
Object.length //1
name
Object.name //"Object"
7、类型查询
typeof是一个一元运算符(就像++,--,!,- 等一元运算符),不是一个函数,也不是一个语句
对变量或值调用 typeof 运算符将返回下列值之一:
object - 如果变量是一种引用类型或 Null 类型的
undefined - 如果变量是 Undefined 类型的
boolean - 如果变量是 Boolean 类型的
number - 如果变量是 Number 类型的
string - 如果变量是 String 类型的
例如:
typeof [1,2,3]; //"object"
typeof null; //"object"
typeof NaN; //"number"
typeof 1; //"number"
typeof undefined; //"undefined"
typeof 'a'; //"string"
typeof true; //"boolean"
8、运算符
1、算数运算符
+ - * / % ++ --
2、比较运算符
> >= < <= != == === !==
注意:双等号比较的是值是否相同,三等号比较的是值和类型是否都相同
1 == "1" // true
1 === "1" // false
3、逻辑运算符
and or not
&& || !
4、赋值运算符
= += -= *= /=