作用域
局部作用域
- 局部作用域分为函数作用域和块级作用域
函数作用域
-
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
xml<script> // 声明 counter 函数 function counter(x, y) { // 函数内部声明的变量 const s = x + y console.log(s) // 18 } // 设用 counter 函数 counter(10, 8) // 访问变量 s console.log(s)// 报错 </script>
-
函数的参数也是函数内部的局部变量
-
不同函数内部声明的变量无法互相访问
-
函数执行完毕后,函数内部的变量实际被清空了
块级作用域
- 在JS中使用{}包裹的代码块称为块级作用域,代码块内部声明的变量外部将【有可能】无法被访问
- let声明的变量会产生块作用域,var不会产生块作用域
- const声明的常量也会产生块作用域
- 不同代码块之间的变量无法互相访问
闭包
-
函数内部返回的函数使用了函数内部顶层的参数
-
在函数执行完毕后,函数内部参数和内部包含的函数不会被立刻释放,内部函数依旧可以打印
-
闭包可以实现数据私有但可能引起内存泄漏
-
使用场景如计数器:
scssfunction fn1() { let count = 0 const fn2 = ()=>{ count++ console.log(count) } return fn2 } let f = fn1() f() f() f() f()
var let const
-
var定义的变量和规范function定义的会产生变量提升
xml<script> // 调用函数 foo() // 声明函数 function foo() { console.log('声明之前即被调用...') } // 不存在提升现象 bar() // 错误 var bar = function () { console.log('函数表达式不存在提升现象...') } </script>
-
例如var a = 1;只将var a提升,赋值不提升
-
let const不会产生变量提升
javascriptobj.fn() function func(){ console.log(this.a) } const obj ={ a:1, fn:function(){ console.log(this) func() } } var a = 2 等价于 var a function func(){ console.log(this.a) } const obj ={ a:1, fn:function(){ console.log(this) func() } } a = 2 obj.fn()
this
-
全局this是指向window对象
scssfunction fn() { console.log(this) } fn()//window
-
谁调用this就指向谁
javascriptfunction fn1() { console.log(this) } let obj = { fn1:fn1 } obj.fn1()//obj function func(){ console.log(this.a) } const obj ={ a:1, fn:function(){ console.log(this) func() } } var a = 2 obj.fn()//obj
-
定时器的this永远指向window(不管谁调用)
javascriptconst obj = { fn:function(){ console.log(this) } } setTimeout(obj.fn,1000)//window
-
箭头函数的this永远指向上一个正常function指向的this
javascriptvar name = 'tom' const obj = { name: 'zc', intro:function () { return () => { console.log('My name is ' + this.name) } }, intro2:function () { return function() { console.log('My name is ' + this.name) } } } obj.intro2()()//my name is tom obj.intro()()//my name is zc
函数参数
动态参数arguments
-
arguments是一个伪数组
-
arguments的作用是动态获取函数的实参
scss<script> // 求和函数,计算所有参数的和 function sum() { // console.log(arguments) let s = 0 for(let i = 0; i < arguments.length; i++) { s += arguments[i] } console.log(s) } // 调用求和函数 sum(5, 10)// 两个参数 sum(1, 2, 4) // 两个参数 </script>
剩余参数...
-
...是语法符号,置于最末函数形参之前,用于获取多余的实参
-
借助...获取的剩余实参是个真数组
xml<script> function config(baseURL, ...other) { console.log(baseURL) // 得到 'http://baidu.com' console.log(other) // other 得到 ['get', 'json'] } // 调用函数 config('http://baidu.com', 'get', 'json'); </script>
箭头函数
-
箭头函数属于表达式函数,不存在函数提升
-
箭头函数只有一个参数时可以省略()
-
箭头函数函数体只有一行代码时可以省略{},并自动作为返回值被返回
xml<script> // const fn = function () { // console.log(123) // } // 1. 箭头函数 基本语法 // const fn = () => { // console.log(123) // } // fn() // const fn = (x) => { // console.log(x) // } // fn(1) // 2. 只有一个形参的时候,可以省略小括号 // const fn = x => { // console.log(x) // } // fn(1) // // 3. 只有一行代码的时候,我们可以省略大括号 // const fn = x => console.log(x) // fn(1) // 4. 只有一行代码的时候,可以省略return // const fn = x => x + x // console.log(fn(1)) // 5. 箭头函数可以直接返回一个对象 // const fn = (uname) => ({ uname: uname }) // console.log(fn('刘德华')) </script>
-
箭头函数中没有 arguments,只能使用 ...动态获取实参
xml<script> // 1. 利用箭头函数来求和 const getSum = (...arr) => { let sum = 0 for (let i = 0; i < arr.length; i++) { sum += arr[i] } return sum } const result = getSum(2, 3, 4) console.log(result) // 9 </script>
解构赋值
数组解构
-
变量的顺序对应数组单元值的位置依次进行赋值操作
-
变量的数量大于单元值数量时,多余的变量将被赋值为 undefined
-
变量的数量小于单元值数量时,可以通过...获取剩余单元值,但只能置于最末位
xml<script> // 普通的数组 let arr = [1, 2, 3] // 批量声明变量 a b c // 同时将数组单元值 1 2 3 依次赋值给变量 a b c let [a, b, c] = arr console.log(a); // 1 console.log(b); // 2 console.log(c); // 3 </script>
对象解构
xml
<script>
// 普通对象
const user = {
name: '小明',
age: 18
};
// 批量声明变量 name age
// 同时将数组单元值 小明 18 依次赋值给变量 name age
const {name, age} = user
console.log(name) // 小明
console.log(age) // 18
</script>
多维解构
xml
<script>
// 1. 这是后台传递过来的数据
const msg = {
"code": 200,
"msg": "获取新闻列表成功",
"data": [
{
"id": 1,
"title": "5G商用自己,三大运用商收入下降",
"count": 58
},
{
"id": 2,
"title": "国际媒体头条速览",
"count": 56
},
{
"id": 3,
"title": "乌克兰和俄罗斯持续冲突",
"count": 1669
},
]
}
// 需求1: 请将以上msg对象 采用对象解构的方式 只选出 data 方面后面使用渲染页面
// const { data } = msg
// console.log(data)
// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数
// const { data } = msg
// msg 虽然很多属性,但是我们利用解构只要 data值
function render({ data }) {
// const { data } = arr
// 我们只要 data 数据
// 内部处理
console.log(data)
}
render(msg)
// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render({ data: myData }) {
// 要求将 获取过来的 data数据 更名为 myData
// 内部处理
console.log(myData)
}
render(msg)
</script>
forEach遍历数组(索引号参数可写可不写)
xml
<body>
<script>
// forEach 就是遍历 加强版的for循环 适合于遍历数组对象
const arr = ['red', 'green', 'pink']
const result = arr.forEach(function (item, index) {
console.log(item) // 数组元素 red green pink
console.log(index) // 索引号
})
// console.log(result)
</script>
</body>
filter筛选数组(索引号参数可写可不写)
xml
<script>
const arr = [10, 20, 30]
// const newArr = arr.filter(function (item, index) {
// // console.log(item)
// // console.log(index)
// return item >= 20
// })
// 返回的符合条件的新数组
const newArr = arr.filter(item => item >= 20)
console.log(newArr)
</script>
对象
构造函数
-
专门用于创建对象的函数,使用new关键字调用
-
使用 new 关键字调用函数的行为被称为实例化
-
实例化构造函数时没有参数时可以省略 ()
-
构造函数的返回值即为新创建的对象
-
构造函数内部的 return 返回的值无效!
xml<script> // 定义函数 function foo() { console.log('通过 new 也能调用函数...'); } // 调用函数 new foo; </script>
实例成员
-
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
-
构造函数内部 this 实际上就是实例对象,为其动态添加的属性和方法即为实例成员
xml<script> // 构造函数 function Person() { // 构造函数内部的 this 就是实例对象 // 实例对象中动态添加属性 this.name = '小明' // 实例对象动态添加方法 this.sayHi = function () { console.log('大家好~') } } // 实例化,p1 是实例对象 // p1 实际就是 构造函数内部的 this const p1 = new Person() console.log(p1) console.log(p1.name) // 访问实例属性 p1.sayHi() // 调用实例方法 </script>
静态成员
-
静态成员指的是添加到构造函数本身的属性和方法
-
一般公共特征的属性或方法静态成员设置为静态成员
-
静态成员方法中的 this 指向构造函数本身
xml<script> // 构造函数 function Person(name, age) { // 省略实例成员 } // 静态属性 Person.eyes = 2 Person.arms = 2 // 静态方法 Person.walk = function () { console.log('^_^人都会走路...') // this 指向 Person console.log(this.eyes) } </script>
内置构造函数
Object
- 推荐使用字面量方式声明对象,而不是 Object 构造函数
- Object.assign 静态方法创建新的对象
- Object.keys 静态方法获取对象中所有属性
- Object.values 表态方法获取对象中所有属性值
Array
- forEach:遍历数组
- filter:过滤数组【筛选数组元素,并生成新数组】
- map:迭代数组【返回新数组,新数组里面的元素是处理之后的值】
- reduce:累加器【返回函数累计处理的结果,经常用于求和等】
- reduce执行过程:1,如果没有起始值,则上一次值是数组的第一个数组元素的值;2,每一次循环,把返回值作为下一次循环的上一次值;3,如果有起始值,则起始值作为上一次值
- 数组常用方法
string常用方法
原型对象
-
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象
-
这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
-
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
-
构造函数和原型对象中的this 都指向 实例化的对象
xml<script> function Person() { // 此处未定义任何方法 } // 为构造函数的原型对象添加方法 Person.prototype.sayHi = function () { console.log('Hi~'); } // 实例化 let p1 = new Person(); p1.sayHi(); // 输出结果为 Hi~ </script>
-
构造函数 Person 中定义与原型对象中相同名称的方法,这时实例对象调用则是构造函中的方法 sayHi:
xml<script> function Person() { // 此处定义同名方法 sayHi this.sayHi = function () { console.log('嗨!'); } } // 为构造函数的原型对象添加方法 Person.prototype.sayHi = function () { console.log('Hi~'); } let p1 = new Person(); p1.sayHi(); // 输出结果为 嗨! </script>
-
当访问对象的属性或方法时,先在当前实例对象是查找,然后再去原型对象查找,并且原型对象被所有实例共享
constructor属性
- 每个原型对象里面都有个constructor 属性(constructor 构造函数)
- 作用:该属性指向该原型对象的构造函数
- 使用场景:如果有多个对象的方法,我们可以给原型对象采取对象形式赋值.但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了,此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
对象原型
- 对象都会有一个属性 proto 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype原型对象的属性和方法,就是因为对象有 proto 原型的存在。
- [[prototype]]和__proto__意义相同
- 用来表明当前实例对象指向哪个原型对象prototype
- __proto__对象原型里面也有一个 constructor属性,指向创建该实例对象的构造函数
原型继承
xml
<script>
// 继续抽取 公共的部分放到原型上
// const Person1 = {
// eyes: 2,
// head: 1
// }
// const Person2 = {
// eyes: 2,
// head: 1
// }
// 构造函数 new 出来的对象 结构一样,但是对象不一样
function Person() {
this.eyes = 2
this.head = 1
}
// console.log(new Person)
// 女人 构造函数 继承 想要 继承 Person
function Woman() {
}
// Woman 通过原型来继承 Person
// 父构造函数(父类) 子构造函数(子类)
// 子类的原型 = new 父类
Woman.prototype = new Person() // {eyes: 2, head: 1}
// 指回原来的构造函数
Woman.prototype.constructor = Woman
// 给女人添加一个方法 生孩子
Woman.prototype.baby = function () {
console.log('宝贝')
}
const red = new Woman()
console.log(red)
// console.log(Woman.prototype)
// 男人 构造函数 继承 想要 继承 Person
function Man() {
}
// 通过 原型继承 Person
Man.prototype = new Person()
Man.prototype.constructor = Man
const pink = new Man()
console.log(pink)
</script>
原型链
- 基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链 ① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
② 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)
③ 如果还没有就查找原型对象的原型(Object的原型对象)
④ 依此类推一直找到 Object 为止(null)
⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
JSON
- JSON是一个字符串
- JSON.parse(str1)--将json字符串转换为对象
- JSON.stringify(obj)--将对象转换为字符串
深浅拷贝
-
浅拷贝:拷贝基础类型数据没有问题,但是拷贝对象会彼此影响(只能拷贝对象地址,指向同一个对象,一改全改)
-
深拷贝方法:递归拷贝;JSON序列化;lodash工具
-
JSON深拷贝:
javascriptlet obj2 = JSON.parse(JSON.stringify(obj1))
改变this指向
call
-
call 方法能够在调用函数的同时指定 this 的值
-
使用 call 方法调用函数时,第1个参数为 this 指定的值
-
call 方法的其余参数会依次自动传入函数做为函数的参数
xml<script> // 普通函数 function sayHi() { console.log(this); } let user = { name: '小明', age: 18 } let student = { name: '小红', age: 16 } // 调用函数并指定 this 的值 sayHi.call(user); // this 值为 user sayHi.call(student); // this 值为 student // 求和函数 function counter(x, y) { return x + y; } // 调用 counter 函数,并传入参数 let result = counter.call(null, 5, 10); console.log(result); </script>
apply
-
apply 方法能够在调用函数的同时指定 this 的值
-
使用 apply 方法调用函数时,第1个参数为 this 指定的值
-
apply 方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数
xml<script> // 普通函数 function sayHi() { console.log(this) } let user = { name: '小明', age: 18 } let student = { name: '小红', age: 16 } // 调用函数并指定 this 的值 sayHi.apply(user) // this 值为 user sayHi.apply(student) // this 值为 student // 求和函数 function counter(x, y) { return x + y } // 调用 counter 函数,并传入参数 let result = counter.apply(null, [5, 10]) console.log(result) </script>
bind
-
bind 方法并不会调用函数,而是创建一个指定了 this 值的新函数
xml<script> // 普通函数 function sayHi() { console.log(this) } let user = { name: '小明', age: 18 } // 调用 bind 指定 this 的值 let sayHello = sayHi.bind(user); // 调用使用 bind 创建的新函数 sayHello() </script>