一、变量类型与计算
1、变量类型和计算:
-
值类型(栈存储),
let a // undefined let a = 100 let b = true let str = "123asdf" let s = Symbol('s')
-
引用数据类型(堆存储)
const obj = {x:100} const arr = [1,2,3] const null = undefined function test (){ }
2、深拷贝与浅拷贝
- 如何区分:
A ,当B复制了A,如果A变化B也变化,就是浅拷贝(拿人手短);A变化B不变化就是深拷贝(自食其力)
so, 浅拷贝拷贝的是地址, 深拷贝拷贝的是值
-
- 如果是基本数据类型,名字和值都会存储在栈内存中
-
- 如果是引用数据类型,名字存在栈内存中,值存在堆内存中,但是栈内存指向堆内存中的值
详细参考:www.jianshu.com/p/1c142ec2c…
- 深拷贝的一个常见场景 element UI 联级选择器el-cascader 绑定的是二维数据,如果想要更改为字符串形式,需:
需要将v-model绑定的值进行深拷贝,这样就不会影响原来数组数据
//深拷贝需要安装lodash依赖 然后导入
import _deep from 'lodash'
//使用:
const region = _deep.cloneDeep(val)
region = region.join(',')
3、手写深拷贝 (重点)
typeof运算符
//判断函数
typeof console.log //function
typeof function () {} //function
//能识别引用类型
typeof null // 'object'
typeof ['a','b'] // 'object'
typeof {x:100} // 'object'
补充:
node全局安装http-server通过端口启动html文件 :
npm install http-server -g
http-server -p 8001
//手写深拷贝
let obj1 = {
age: 20,
name: "张三",
address: true,
city: {
name: "北京"
},
children: [1, 2, 3]
}
let obj2 = cloneData(obj1)
function cloneData(data) {
if (typeof data == null || typeof data !== 'object') {
return data
}
let result
if (data instanceof Array) {
result = []
} else {
result = {}
}
//对象和数组都可以用for in 来遍历
for (key in data) {
//保证key不是原型的属性
if (data.hasOwnProperty(key)) {
result[key] = cloneData(data[key])
}
}
return result
}
4、类型转换
-
字符串拼接
const a = 100 + 10 //110 const b = 100 = '10' //10010 const c = true + '10' //true10
-
==
100 = '100' //true 0 == '' //true 0 == false //true false =='' //true null == undefined //true
//除了 == null 之外,其他一律用 === ,例如 const obj = { x:100} if(obj.a == null){} //相当于: if(obj.null === null || obj.null === undefined)
-
if语句和逻辑运算
-
truly变量
!!a === true
-
falsely变量
!!a === false
-
以下是false变量,其他都是truly变量
!!null === false !!undefined === false !!'' === false !!NaN === false !!false === false !!0 === false
-
-
变量类型相关的面试题
const obj1 = {x:100,y:200} let obj2 = obj1 let x1 = obj1.x obj2.x = 101 x1 = 102 console.log(obj1) // {X:101}
二、原型和原型链
1、如何用class实现继承
class实质上是函数 ,可见是语法糖
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name}会吃饭`);
}
}
class Student extends People { //student继承了people类
constructor(name, number) {
super(name) //属性需要用name继承
this.number = number
}
study() {
console.log(`学号是${this.number},正在学习...`);
}
}
zs = new Student('张三', '00001') //通过new关键字实例化了类student
2、原型关系
- 显示原型: 每个class都有显示原型 : 如
Person.prototype
- 隐式原型: 每个实例都有隐式原型: 如
zs.__proto__
- 实例的隐式原型=》指向class的显示原型
3、基于原型的执行规则
- 获取属性(
zs.name
)或者执行方法时(zs.eat()
)时, - 先从自身属性和方法上找,
- 找不到自动去它的原型(
zs.__ropto__
)身上找
4、原型链
对象实现继承的一条规则
Object.prototype.__proto__ === null
People.prototype.__proto__ === Object.prototype
Student.prototype.__proto__ === People.prototype
zs.__proto__ === Student.prototype
zs.constructer.prototype===zs.__prpto__
三、作用域和闭包
-------------待更新-----------
1、作用域
- 全局作用域
- 函数作用域 注意:在函数内用var声明的变量是全局变量,不会销毁
- 块级作用域{} (es6新增)
2、闭包
-
函数作为参数被传递
function create(fn) { let i = 100 fn() } let i = 200 let inner = function() { //函数定义向上找 console.log(i); // i = 200 } create(inner)
-
函数作为返回值被返回
function create(){ let i = 100 return function(){ //函数定义向上找 console.log(i) } } let fn = create() let i = 200 fn() // i = 100
总结:闭包中自由函数的寻找,是在函数定义的地方向上级作用域找,而不是在执行的地方
3、this有几种赋值情况
this取什么值是在函数执行的时候确认的,不是在函数定义的时候确认的
this被谁调用就指向谁,如果没有人调用就指向window
箭头函数this的取值取的是他上级作用域的值
1、this指向问题:
-
一般函数中的this指向window
let test = function(){ console.log(this, drink + ',' + fruits); //window } test("可乐", '橘子')
-
严格模式下 ’ use strict ‘,this指向undefined
-
对象中的this指的是当前对象
let obj = { name: "李小明", eat(drink, fruits) { console.log("吃", this); //对象中的this指向当前对象 }, } obj.eat()
-
构造函数中的this指定是当前实例
function student(name) { this.name = name this.study = function() { console.log(this.name + "会学习"); //构造函数中的this指的是当前实例 setTimeout(function() { console.log(this); //this指向window }) setTimeout(function() { console.log(this); //添加bind,传入当前实例this,结果里边的 this指向当前实例 }.bind(this)) setTimeout(() => { console.log(this); //this指向上级作用域的值-->当前实例 }) } } let zhangs = new student('张三') zhangs.study()
2、如何改变this的指向:
-
call (this,arg1,arg2,...) : 其中第一个参数this是指定的上下文,arg1,arg2为函数所需传入的参数
-
apply(this,[arg1,arg2,...]) : 用法与call一样,区别在于apply的第二个参数是个数组
-
bind(): 会创建一个新的函数,叫做绑定函数。第一个参数作为this , 第二个参数及以后的参数加上绑定函数运行时本身的函数作为原函数的参数来调用函数
let food = { name: "食物" } let test = function(drink, fruits) { console.log(this, drink, fruits); } test.call(food,"果汁","橘子") test.apply(food,["果汁","橘子"]) let newtest = test.bind(food,'可乐') newtest('橙子')
-
手写bind函数
---------------待更新-------