1.对象
是一组“键值对”(key-value)的集合,是一种无序的复合数据集合
let obj ={
a:1,
b:2
}
obj对象包含两个键值对,第一个键名a,键值1。第二个键名b,键值2。两个键值对逗号分隔。
键名可以是数组,但是会被自动转换为字符串。
let obj ={
1p:'hello'
} //报错
let obj={
'1p':'hello',
'w z':' 1hello'
}//不报错
如果键名不符合标识名的条件,必须加上引号。
2.对象的引用
如果不同的变量指向同一个对象,那么它们都指向同一个内存地址。修改其中一个变量,会影响其他所有的变量
let a={}
let b=a
b.p=1
a.p //1
a和b指向同一个对象,a的值变为1,不影响b的值。b还是指向原来那个对象。
let a={}
let b=a
a=1
b//{}
3.属性的读取
读取对象属性时,有两种方法,一种是使用 点运算符
,另外一种是方括号
let obj ={
a:'hello'
}
obj.a //hello
obj['a'] //hello
使用方括号运算符时,键名必须放在引号里面,否则会被当做变量处理。
let obj={
a:1,
0.7:'hello'
}
obj['a'] //1
obj[0.7] //hello
obj.0.7 //报错
对象数字键可以不加引号,会自动转换为字符串。数字键不能使用点运算符 会报错。
4.属性的赋值
let obj ={}
obj.p=1
p // 1
查看对象本事的所有属性,可以使用Object.keys方法
let obj ={
a:1,
b:2
}
Object.keys(obj)
// ['a','b']
5.属性的删除:delete命令
delete命令用于删除对象的属性,删除成功后返回true
let obj ={
a:1
}
delete obj.a //true
obj.a //undefined
Object.keys(obj) //[]
delete命令删除成功后,再读取该属性就会返回 undefined 查看对象所有方法也会不存在该属性。
let obj ={}
delete obj.a //true
delete 删除不存在对象的属性 也会返回true ,不能根据返回true 来判断是否删除成功。
let obj ={}
delete obj.toString() //true
obj.toString() // function toString() { [native code] }
toString
是对象obj
继承的属性,虽然delete
命令返回true
,但该属性并没有被删除,依然存在。这个例子还说明,即使delete
返回true
,该属性依然可能读取到值。
6.属性是否存在:in运算符
用于检测 属性是否在 对象上,在 返回true 不在返回false
let obj={
a:1
}
'a' in obj //true
'toString' in obj//true
obj本身不存在toString
属性,in运算符检测 不管是自身 还是继承的都会返回true,toString
是继承属性。
可以通过 hasOwnProperty
方法判断是否是自身属性
let obj ={}
if(toString in obj){
console.log(obj.hasOwnProperty('toString'))//false
}
7.for...in循环
let obj={a:1}
for(let i in obj){
console.log('键名':i)
console.log('键值':obj[i])
}
// 键名 :a
// 键值:1
for...in循环两个注意点:
1.遍历自身所有可以遍历的属性,会跳过不可遍历的属性
2.还可以遍历继承属性
8.with语句
with(对象){
语句
}
作用: 操作同一个对象 多个属性。提供书写的方便
let obj={
a:1,
b:2
}
with(obj){
a=2,
b=4
}
//等同于
obj.a=2
obj.b=4
with只能操作对象里面已有的属性,否则会创建当前作用的全局变量
let obj={}
with(obj){
a=4
}
obj.a //undefined
a//4
9.函数
javaScript
有两种声明函数的方法
1.声明式
function fn(){
console.log("我是声明式的函数")
}
fn()
2.表达式式(匿名式) 函数也是一种数据类型
var fn = function(){
console.log("我是表达式式的函数")
}
fn()
如果同一个函数被多次声明,后面的声明就好覆盖前面的声明
function fn(){
console.log(11)
}
function fn(){
console.log(22)
}
fn() // 22
10.圆括号运算符,return语句和递归
调用函数时,要使用圆括号运算符,圆括号之中,可以加入函数的参数
function obj(x,y){
return x+y
}
obj(1,2) //3
函数体内部 return
语句,表示返回,js遇到return语句,就会直接返回return后面的表达式的值,后面还有语句也不会执行。return不是必须的,如果没有该函数就没有返回值,或者说返回undefined
11.函数名的提升
JavaScript 引擎将函数名视同变量名,所以采用function
命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
fn()
function fn(){}
12.toString()
函数的toString()
返回一个字符串,内容时函数的源码
function fn(){
a:1,
b:'hello'
}
fn.toString()
//function fn(){
// a:1,
// b:'hello'
//}
原生的函数,toString()
方法返回function(){[native code]}
Math.sqrt.toString()
// "function sqrt(){ [native code]}"
Math.sqrt
是javaScript引擎提供的原生函数,toString() 方法就是返回原生代码的提示
13.函数作用域
变量存在的范围。一种全局作用域,所有地方可读。另一种是函数作用域,变量只在函数内部存在,还有一种块级作用域,比如if语句 循环语句中 声明的变量。
let abc=1
function fn(){
let a1=2
console.log(abc)
console.log(a1)
}
fn() // 全局变量abc
// 函数作用域a1
14.函数内部的变量提升
与全局作用域一样,函数作用域内部也会产生“变量提升”现象
function fn(a){
if(a>10){
let num=a-10
}
}
等同于
funtion fn(a){
let num
if(a>10){
num=a-10
}
}
15.函数本身的作用域
函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
let a=1
let obj=function(){
console.log(a)
}
function fn(){
let a=2
obj()
}
fn() //1
函数 obj 是在函数 fn 的外部声明的,所有它的作用域绑定再外层,内部变量a不会得到函数 fn体内取值,所以输出是1。
function fn1(){
let x=1
function fn2() {
console.log(x)
}
return fn2
}
let x =2
let obj=fn1()
obj() //1
fn1 函数内部声明了一个函数 fn2 ,fn2的作用域绑定fn1。当我们再fn1外部取出 fn2执行时,变量X指向 fn1内部的x,而不是fn1外部的x。这种机制 称为 ‘ 闭包 ’
16.函数参数
函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。
function obj(x) {
return x * x;
}
obj(2) // 4
obj(3) // 9
函数参数不是必需的,JavaScript 允许省略参数。
17.传递方式
函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递。这意味着,在函数体内修改参数值,不会影响到函数外部。
let p = 2;
function fn(p) {
p = 3;
}
fn(p);
p // 2
变量p
是一个原始类型的值,传入函数f
的方式是传值传递。因此,在函数内部,p
的值是原始值的拷贝,无论怎么修改,都不会影响到原始值
如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。
let obj = { p: 1 };
function fn(o) {
o.p = 2;
}
fn(obj);
obj.p // 2
传入函数fn
的是参数对象obj
的地址。因此,在函数内部修改obj
的属性p
,会影响到原始值。
如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。
let obj = [1, 2, 3];
function fn(o) {
o = [2, 3, 4];
}
fn(obj);
obj // [1, 2, 3]
18.同名参数
如果有同名的参数,则取最后出现的那个值。
function fn(a,a){
console.log(a)
}
fn(1,2)//2
19.arguments对象
arguments
可以在函数体内部读取所有参数。arguments[0]:就是第一个参数,arguments[1]就是第二个参数,只有在函数体内部,才可以使用。
let obj=function (a){
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
obj(1,2,3)
arguments很像数组,但数组的方法不能直接使用,如果需要使用,必须先转为真正的数组。 两种常用的方法: slice方法 逐一添加到新数组。
let arr=Array.prototype.slice.call(arguments)
//或者
let arr=[]
for(let i=0;i<arguments.length;i++){
arr.push(arguments[i])
}
20.闭包
闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
function fn(a){
return function(){
return a++
}
}
let obj=a(5)
obj()//5
obj()//6
obj()//7 每次调用该函数 都会在上一次调用基础上计算。
闭包的另一个用处,是封装对象的私有属性和私有方法。
function Person(name) {
let age;
function setAge(n) {
age = n;
}
function getAge() {
return age;
}
return {
name: name,
getAge: getAge,
setAge: setAge
};
}
let p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25
注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
21.立即调用函数表达式
圆括号开头后面跟一个表达式
( function(){}());
//或者 分号必须的,省略会连着一起
(function(){})();
目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
// 写法一
let tmp = newData;
processData(tmp);
storeData(tmp);
// 写法二
(function () {
let tmp = newData;
processData(tmp);
storeData(tmp);
}());
22.数组
数据的有序集合,可以用来存储多个数据
数组 创建的两种方式
1.构造函数方式:let arr = new Array()
let arr = new Array(); //创建了一个空的数组
console.log(arr); // []
2.字面量
let arr = [1,'string',true,undefined,null,[],{},function(){}]
//数组中的成员(元素)可以是任意的数据类型
数组的索引(下标):从 0 开始
数组的的元素的访问:
let arr = ['胖虎','静香','大雄']
console.log(arr[2]) //'大雄'
23.length 属性
数组的length
属性,返回数组的成员数量。
let arr=['a','b']
arr.length //2
arr[2]='c'
arr.length//3
arr[9]='d'
arr.leng//10
length
属性的值总数比最大的那个整数键大1。数组是一种动态的数据结构,可以随时增减数组成员。
length 属性是可写的。若认为设置小于当前成员个数的值,该数组的成员数量会自动减少到 length设的值
let arr = [ 'a', 'b', 'c' ];
arr.length // 3
arr.length = 2;
arr // ["a", "b"]
上述代码,length属性设为2(最大的整数键只能是1),那么整数键 2(值为c),就不存在数组中,会被自动删除。
清空数组的一个有效方法,就是将 length 属性设为0。