变量类型和计算
1. 值类型与引用类型
值类型:字符串string,数值number,布尔值boolean, null,undefined,symbol
引用类型:对象 Object,数组Array,函数Function
考点
- 存储方式,值类型的数据是存储在栈中,引用类型的数据存储在堆中,栈中存储的是在数据在堆中的地址。
- 引用类型的赋值,赋值的是变量的存储地址,修改其中一个变量的属性时,会对另一个变量造成影响。(这就是浅拷贝)
var person = { name: 'Mary', sex: 21 };
var person1 = person;
person1.name = 'Json';
console.log(JSON.stringify(person1)); // {"name": "Json", "sex":"21"}
2. typeof 类型判断
typeof 能判断出除null以外的所有值类型,typeof(null)的值为'object',判断数组与函数的值也为'object'。
3. 深拷贝与浅拷贝
浅拷贝:B复制了A,修改A时,B跟着变化。
let a=[0,1,2,3,4],
b=a;
console.log(a===b);//true
a[0]=1;
console.log(a,b)//[1,1,2,3,4]
深拷贝:复制之后是两个独立个体,互不影响。
实现深拷贝的方式
- 使用JSON.parse(JSON.stringify())
var obj1 = {
a: 1,
b: 2,
c: 3
}
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a = 4;
console.log(obj1.a); // 1
console.log(obj2.a); // 4
- 递归
/**
* 深拷贝
*/
const obj1 = {
age: 20,
name: 'xxx',
address: {
city: 'beijing'
},
arr: ['a', 'b', 'c']
}
const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
obj2.arr[0] = 'a1'
console.log(obj1.address.city)//beijing
console.log(obj1.arr[0])//a
/**
* 深拷贝
* @param {Object} obj 要拷贝的对象
*/
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是对象和数组,直接返回
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
- Object.assign(target,testArr)
仅限于单层的引用类型进行深拷贝。
var obj1 = {
a: 1,
b: 2,
c: 3
}
var obj2 = Object.assign({}, obj1);
obj2.b = 4;
console.log(obj1.b); // 2
console.log(obj2.b); // 4
var obj1 = {
a: 1,
b: 2,
c: ['a','b','c']
}
var obj2 = Object.assign({}, obj1);
obj2.c[1] = 1;
console.log(obj1.c); // ["a", 1, "c"]
console.log(obj2.c); // ["a", 1, "c"]
变量计算
- 字符串拼接
var a=100+10
var b=100+'10'
console.log(a) // 110
console.log(b) //'10010'
- '==' 运算符
console.log(100=='100') // true
console.log(''==0) // true
console.log(null==undefined) // true
原型与原型链
1. class和继承
js本身没有类的概念,继承是通过原型链来实现的
js继承的本质就是子类共享父类的原型对象
// 类
class Student {
constructor(name, number) {
this.name = name
this.number = number
// this.gender = 'male'
}
sayHi() {
console.log(
`姓名 ${this.name} ,学号 ${this.number}`
)
}
}
// 通过类 new 对象/实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()
const madongmei = new Student('马冬梅', 101)
console.log(madongmei.name)
console.log(madongmei.number)
madongmei.sayHi()
// 父类
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat something`)
}
}
// 子类
class Student extends People {
constructor(name, number) {
super(name)
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} 学号 ${this.number}`)
}
}
// 子类
class Teacher extends People {
constructor(name, major) {
super(name)
this.major = major
}
teach() {
console.log(`${this.name} 教授 ${this.major}`)
}
}
// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()
xialuo.eat()
// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name)
console.log(wanglaoshi.major)
wanglaoshi.teach()
wanglaoshi.eat()
2. 原型与原型链
隐式原型:
console.log(xialuo.__proto__)
显式原型:
console.log(Student.prototype)
console.log(xialuo.__proto__===Student.prototype)//true
- 每个class都有显式原型prototype。
- 每个实例都有隐式原型proto。
- 实例的proto指向对应class的prototype。
实例获取属性或方法时,先在自身中寻找,如果没有找到就到隐式原型中寻找
原型链
instanceof
判断实例是否在原型链中。
console.log(xialuo instanceof People)//true
闭包
闭包是指有权访问另外一个函数作用域中的变量的函数。可以理解为(能够读取其他函数内部变量的函数) 。
闭包的作用: 正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的变量,在函数执行完之后依旧保持没有被垃圾回收处理掉。
// 函数作为返回值
// function create() {
// const a = 100
// return function () {
// console.log(a)
// }
// }
// const fn = create()
// const a = 200
// fn() // 100
// 函数作为参数被传递
function print(fn) {
const a = 200
fn()
}
const a = 100
function fn() {
console.log(a)
}
print(fn) // 100
异步和单线程
单线程
js是单线程语言,只能同时做一件事。
console.log(100);
setTimeout(function(){
console.log(300)
},2000);
console.log(400)
console.log(100)
alert(200)
console.log(300)
异步的使用场景
- 网络请求,如ajax图片加载
- 定时任务,如setTimeout
promise的使用
es6之前使用回调函数来实现异步,有时候会陷入“回调地狱”,例如ajax嵌套,需要用第一层取到的数据去调第二层,依次类推。
promise实现异步加载图片:
function loadImg(src) {
const p = new Promise(
(resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const err = new Error(`图片加载失败 ${src}`)
reject(err)
}
img.src = src
}
)
return p
}
// const url = 'https://img.baidu.com/5a9fc8070001a82402060220-140-140.jpg'
// loadImg(url).then(img => {
// console.log(img.width)
// return img
// }).then(img => {
// console.log(img.height)
// }).catch(ex => console.error(ex))
const url1 = 'https://img.baidu.com/93nsun2sa-img.-140.jpg'
const url2 = 'https://img3.b.com/5a9udiafc8070001a82402060220-100-100.jpg'
loadImg(url1).then(img1 => {
console.log(img1.width)
return img1 // 普通对象
}).then(img1 => {
console.log(img1.height)
return loadImg(url2) // promise 实例
}).then(img2 => {
console.log(img2.width)
return img2
}).then(img2 => {
console.log(img2.height)
}).catch(ex => console.error(ex))