🌊 枚举对象中的属性(for-in语句)
- 枚举属性,指将对象中的所有的属性全部获取
- 枚举属性使用 for-in 语句
- 语法:
for(let propName in 对象){ 语句... }
- for-in 的循环体会执行多次,有几个属性就会执行几次,每次执行时,都会将一个属性名赋值给我们所定义的变量
- 注意:并不是所有的属性都可以枚举,比如:使用symbol(符号)添加的属性,符号添加的属性一般不希望被外界所访问,符号添加的属性不可枚举。
let obj = { name:'孙悟空', age:18, gender:"男", address:"花果山", [Symbol()]:"测试的属性" // 符号添加的属性是不能枚举 } for(let propName in obj){ console.log(propName, obj[propName]) }
🌊 可变类型
- 原始值都属于不可变类型,一旦创建就无法修改,在内存中不会创建重复的原始值
- 对象属于可变类型,对象创建完成后,可以任意的添加删除修改对象中的属性。
- 注意:
- 当对两个对象进行相等或全等比较时,比较的是对象的内存地址
- 如果有两个变量同时指向一个对象,通过一个变量修改对象时,对另外一个变量也会产生影响。当修改一个对象时,所有指向该对象的变量都会受到影响
🌊 对象的修改与变量的修改
- 修改对象:
- 修改对象时,如果有其他变量指向该对象, 则所有指向该对象的变量都会受到影响。如果我们修改存储对象的变量的指向(修改变量),即变量指向为不同的对象,此时通过变量修改对象不会影响其他变量所指向的对象。
- 修改变量:
- 修改变量时,只会影响当前的变量,不会影响其他变量。
- 在使用变量存储对象时,很容易因为改变变量指向的对象,提高代码的复杂度,所以通常情况下,声明存储对象的变量时会使用const。
- 注意:const只是禁止变量被重新赋值,对对象的修改没有任何影响。对于指向对象的变量来说,存储的为对象的地址,只要其指向没变(存储的地址没变)相当于该变量没有修改,修改对象的属性不会影响变量的指向。
🌊 方法(method)
当一个对象的属性指向一个函数,那么我们就称这个函数是该对象的方法,调用函数就称为调用对象的方法
let obj = {} obj.name = "孙悟空" obj.age = 18 // 函数也可以成为一个对象的属性,对象的方法 obj.sayHello = function(){ console.log("hello") } console.log(obj) obj.sayHello() // 调用对象的方法 对象.方法()
🥽 函数
函数(Function):函数也是一个对象,它具有其他对象所有的功能,函数中可以存储代码,且可以在需要时调用这些代码
🌊 函数的创建
语法:
function 函数名(){ 语句... }
// 创建一个函数对象 function fn(){ console.log("你好!") console.log("Hello!") } // 使用typeof检查函数对象时会返回function console.log(typeof fn)
🌊 函数的调用
调用函数就是执行函数中存储的代码,语法:函数名()
。对于函数,调用几次,函数中的代码就会执行几次。
fn() fn() fn()
🌊 函数的创建方式
- 函数的创建方式主要有三种:
- 函数声明
function 函数名(){ 语句... }
- 函数表达式
const 变量 = function(){ 语句... }
- 箭头函数
const 变量 = () => { 语句... } // 如果箭头函数的函数体只有一句语句可以简写 const 变量 = () => 语句
function fn(){ console.log("函数声明所定义的函数~") } const fn2 = function(){ console.log("函数表达式") } const fn3 = () => { console.log("箭头函数") } const fn4 = () => console.log("箭头函数") console.log(typeof fn) console.log(typeof fn2) console.log(typeof fn3) console.log(typeof fn4)
🌊 参数
💦 函数的参数
- 形式参数
- 在定义函数时,可以在函数中指定数量不等的形式参数(形参)
- 在函数中定义形参,就相当于在函数内部声明了对应的变量但是没有赋值。函数形参的赋值,发生在函数调用时实参的传递。
- 函数定义形参语法:
- 函数声明
function 函数名([参数]){ 语句... }
- 函数表达式
const 变量 = function([参数]){ 语句... }
- 箭头函数
const 变量 = ([参数]) => { 语句... }
- 实际参数
- 在调用函数时,可以在函数的()传递数量不等的实参
- 实参会赋值给其对应的形参
- 实参:
- 如果实参和形参数量相同,则对应的实参赋值给对应的形参
- 如果实参多余形参,则多余的实参不会使用
- 如果形参多余实参,则多余的形参为undefined
- 实参的类型
- JS中不会检查参数的类型,可以传递任何类型的值作为参数
function fn(a, b){ console.log("a =", a, "\tb =", b) } fn(1, 2) fn(1, 2, 3) fn(1) fn(true, "hello") fn(null, 11n) fn({name:"孙悟空"},"hello")
- 箭头函数的参数
- 当箭头函数中只有一个参数时,可以省略()
const fn2 = a => { console.log("a =", a); }
💦 函数参数的默认值
定义函数的参数时,可以为参数指定默认值。函数参数的默认值,会在形参没有对应实参传递时生效。
const fn3 = (a=10, b=20, c=30) => { console.log("a =", a); console.log("b =", b); console.log("c =", c); // 由于在调用函数时形参c没有实参传递,所以形参c的值为默认值30 } fn3(1, 2)
💦 对象类型数据作为函数实参
对象类型的数据作为函数的实参,实际上是将保存对象地址变量中的对象地址作为函数的实参传递给函数形参,所以函数的形参得到的为对象的地址,即对象类型的数据作为函数的实参,函数的形参也会指向该对象。
对象类型数据作为函数实参,在函数中对对象的属性进行修改会对对象的属性造成影响。
function f(a) { console.log('f函数中输出: ', a) a.name = 'Tom' console.log('f函数中输出: ', a) } let obj = { name: 'Mary' } f(obj) console.log('f函数外输出: ', obj)
💦 函数作为参数
在JS中,函数也是一个对象(函数被称为一等函数或一等对象,别的对象能做的事情,函数也可以)
function fn(a) { console.log(a) if (typeof a === 'function') { // 因为函数为对象,函数作为实参,a指向函数对象,a可以当前函数调用 a() // 如果传递的实参为函数对象则调用函数 } } function fn2(){ console.log("我是fn2") } fn(fn2) fn(function(){ console.log("我是匿名函数~") }) fn(()=>console.log("我是箭头函数"))
🌊 返回值
💦 函数的返回值
- 在函数中,可以通过return关键字来指定函数的返回值,返回值就是函数的执行结果,函数调用完毕返回值便会作为结果返回
function sum(a, b) { // console.log(a + b) // 计算完成后,将计算的结果返回而不是直接打印 return a + b } let numSum = sum(12, 13) console.log(numSum)
- 任何值都可以作为返回值使用(包括对象和函数之类),如果return后不跟任何值,则相当于返回undefined,如果不写return,那么函数的返回值依然是undefined
function fn1() { return 'hello' } function fn2() { return 123 } function fn3() { return {name: 'Tom'} } function fn4() { function fnInner() { console.log('fn4 fnInner') } return fnInner } function fn5() { return } function fn6() {} console.log('fn1', fn1()) // 打印调用函数后的返回值 console.log('fn2', fn2()) console.log('fn3', fn3()) console.log('fn4', fn4()) console.log('fn5', fn5()) console.log('fn6', fn6())
- return一执行函数立即结束,return后面的代码不会继续执行
function fn(a, b) { let sum = a + b return sum console.log('hello world') console.log('hello world') console.log('hello world') console.log('hello world') } let sum = fn(11, 12) console.log(sum)
💦 箭头函数的返回值
对于箭头函数,如果箭头函数的函数体只有一句语句且返回值为该语句的计算值,则返回值可以直接写在箭头后,如果直接在箭头后设置对象字面量为返回值时,对象字面量必须使用()括起来,否则该对象字面量会被认为是函数体
let add = (a, b) => a + b let fn = () => ({name:"孙悟空"}) let sum = add(12, 23) let result = fn() console.log(sum) console.log(result)
🌊 作用域
💦 作用域简介
- 作用域指的是一个变量的可见区域
- 作用域有两种:
- 全局作用域
- 全局作用域在网页运行时创建,在网页关闭时销毁
- 所有直接编写到script标签中的代码都位于全局作用域中
- 全局作用域中的变量是全局变量,可以在任意位置访问
- 局部作用域
- 块作用域
- 块作用域是一种局部作用域
- 块作用域在代码块执行时创建,代码块执行完毕它就销毁
- 在块作用域中声明的变量是局部变量,只能在块内部访问,外部无法访问
- 函数作用域
- 函数作用域也是一种局部作用域
- 函数作用域在函数调用时产生,调用结束后销毁
- 函数每次调用都会产生一个全新的函数作用域
- 在函数中定义的变量是局部变量,只能在函数内部访问,外部无法访问
💦 作用域链
当我们使用一个变量时,JS解释器会优先在当前作用域中寻找变量,如果找到了则直接使用;如果没找到,则去上一层作用域中寻找,找到了则使用
如果没找到,则继续去上一层寻找,以此类推;如果一直到全局作用域都没找到,则报错 xxx is not defined。
作用域链,就是就近原则,会寻找距离使用位置最近的作用域的变量。
let b = 33 function fn(){ function f1(){ let b = 55 console.log(b) // 当前作用域就有b,所有输出为当前作用域的b } f1() console.log(b) // 当前作用域无b,会向外查找 } fn()
🌊 window对象
- 在浏览器中,浏览器为我们提供了一个window对象,可以直接访问
- window对象代表的是浏览器窗口,通过该对象可以对浏览器窗口进行各种操作,除此之外window对象还负责存储JS中的内置对象(如Number、String)和浏览器的宿主对象(如document、console)
- window对象的属性可以通过window对象访问,也可以直接访问
console.log("hello") window.console.log("world")
- 函数就可以认为是window对象的方法
function fn(){ console.log('我是fn') } window.fn() fn()
- 向window对象中添加的属性会自动成为全局变量
window.a = 10 // 向window对象中添加的属性会自动成为全局变量 console.log(window.a) console.log(a)
- var 用来声明变量,作用和let相同,但是var不具有块作用域
- 在全局中使用var声明的变量,都会作为window对象的属性保存,等价于window.变量
- 使用function声明的函数,都会作为window的方法保存
- 使用let声明的变量不会存储在window对象中,而存在一个秘密的小地方(无法访问)
- var虽然没有块作用域,但有函数作用域
var b = 20 // 等价于window.b = 20 console.log(window.b) console.log(b) console.log('------------------') function fn(){ console.log('我是fn') } window.fn() fn() console.log('------------------') let c = 33 window.c = 44 console.log(c) // window与存放let变量的位置都有相同的变量,优先let声明的变量 console.log(window.c) console.log('------------------') function fn2(){ var d = 10 // var虽然没有块作用域,但有函数作用域 e = 200 // 在局部作用域中,如果没有使用var或let声明变量,则变量会自动成为window对象的属性 也就是全局变量 } fn2() console.log('e:', e) console.log('d:', d)
🌊 提升
💦 变量的提升
使用var声明的变量,它会在所有代码执行前被声明,所以我们可以在变量声明前就访问变量,但是没什么意义,一般都在变量声明赋值后进行变量的访问。
console.log(a) var a = 10 // 相当于 var a console.log(a) // undefine a = 10
let声明的变量实际也会提升,但是在赋值之前解释器禁止对该变量的访问
💦 函数的提升
使用function函数声明创建的函数,会在其他代码执行前被创建,所以我们可以在函数声明前调用函数
fn() function fn(){ alert("我是fn函数~") } // 相当于 function fn() { alert("我是fn函数~") } fn()
// 会报错 fn2() var fn2 = function(){} // 相当于 var fn2 // 此时fn2为undefine fn2() fn2 = function(){}