js基础部分
01 值类型与引用类型
1.1 问法
- js判断数据类型?
- js值类型与引用类型有哪些?
- 值类型与引用类型的区别?
1.2 介绍
JavaScript存储数据两个区域:栈和堆
栈:通常空间是固定的(占据空间小、大小固定)
堆:通常空间是动态的分配(占据空间大,大小不固定)
ES5值类型:Boolean(布尔值)、String(字符串)、Number(数字)、Undefined(未定义)、Null(空(特殊))
ES5引用类型:Object(对象)、Array(数组)、Function(函数)
ES6值类型:Symbol(符号)
ES6引用类型:Map(图)、Set(集合)、Promise(承诺)
1.3 code分析
- 值类型
var a = 15; //值类型
var b = a; //把a的值赋值给b
b = 50;
console.log(a); //15
console.log(b); //50
可以这样理解:
栈 | 栈 |
---|---|
key | value |
a | 15 |
b | 50 |
- 引用类型
var d = {age:18};//d 是引用类型 key存储在栈中 真正的值存储在堆中
var e = d; //把d 的引用地址赋值给e,(e与d都指向同一块存储地址)
e.age = 35;
console.log(d.age);//35
可以这样理解:
栈 | 栈 | 堆 | |
---|---|---|---|
key | 地址 | ||
d | {age:18} | {age:18} | |
e | {age:18} | ||
当e.age = 35;执行之后 | |||
d | {age:35} | {age:35} | |
e | {age:35} |
1.4 回答
- 值类型与引用类型的区别?
答:
-
值类型
key
与value
都存储在js
内存栈中; -
引用类型key存储在栈中,真正的值存储在堆中;
-
把引用类型赋值给一个变量,其实是把变量的引用类型的地址指向引用类型堆中地址。
- 值类型与引用类型有哪些?
答:
- 值类型(或简单数据类型):String、Number、Boolean、Symbol、Undefined、Null
- 引用类型(复杂数据类型):Array、Object、Function、Set、Map
02 数据类型检测的方式
2.1 typeof
typeof
只能判断 是否为值类型或者引用类型,引用类型除函数都返回的是 object
- code
console.log(typeof "abc"); //string
console.log(typeof 123); //number
console.log(typeof true); //boolean
console.log(typeof undefined); //undefined
console.log(typeof null); // Object(null 空指针)
console.log(typeof function(){}); //function
console.log(typeof {}); //object
console.log(typeof []); //object
typeof new Map() //'object'
typeof new Set() //'object'
typeof Symbol //'function'
typeof Symbol() //'symbol'
其中数组、对象、null都会被判断为 object
2.2 instanceof
instanceof
含义:是不是某个对象的实例(是数组还是对象。
instanceof
只能正确判断引用数据类型,而不能判断基本数据类型。instanceof
运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的 prototype
属性。
- code
var a = [1,2,3];
console.log(a instanceof Array); // true a是数组的实例
console.log(a instanceof Object);//true a是数组的实例,数组也是对象object
var b = {age:18};
console.log(b instanceof Array); //false b不是数组的实例
console.log(b instanceof Object); //true b是对象的实例
- 控制台打印
2.3 constructor
constructor 有两个作用:
- 判断数据的类型
- 对象实例通过constructor对象访问它的构造函数。
需要注意的是:如果创造一个对象来改变它的原型,constructor就不能用来判断数据类型了;
- code
console.log((2).constructor === Number); //true
console.log((true).constructor === Boolean); //true
console.log(("str").constructor === String); //true
console.log(([]).constructor === Array); //true
console.log((function(){}).constructor === Function); //true
console.log(({}).constructor === Object); //true
var a = [1,2,3];
console.log(a.constructor === Array); // true
console.log(a.constructor === Object);//false
var b = {age:18};
console.log(b.constructor === Array); //false
console.log(b.constructor === Object); //
- 控制台打印
数组专用 Array.isArray()
- Array.isArray()用于确定传递的值是否是一个Array
- 语法
Array.isArray(value)
-
参数
value
需要检测的值。 -
返回值
如果值是
Array
,则为true;否则为false。 -
code
// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array('a', 'b', 'c', 'd'))
Array.isArray(new Array(3));
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype);
// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray(new Uint8Array(32))
Array.isArray({ __proto__: Array.prototype });
2.4 Object.prototype.toString.call()
Object.prototype.toString.call()
最准确判断数据类型方法。
Object.prototype.toString.call()
使用Object对象的原型方法toString 来判断数据类型。
- code
var a = Object.prototype.toString;
console.log(a.call(2)); //'[object Number]'
console.log(a.call("str")); //'[object String]'
console.log(a.call(true));//'[object Boolean]'
console.log(a.call([])); //'[object Array]'
console.log(a.call(function(){})); //'[object Function]'
console.log(a.call(undefined)); //'[object Undefined]'
console.log(a.call(null)); //'[object Null]'
console.log(a.call(/ /)); //'[object RegExp]'
console.log(a.call(document.body)); //'[object HTMLBodyElement]'
- 控制台打印
- 封装一个函数
// Object.prototype.toString.call()
function getType(obj){
return Object.prototype.toString.call(obj).sclice(8,-1);
}
// Object对象 protype原型.toString转换为字符串 slice分割 从第8位到-1位
03 深拷贝与浅拷贝
3.1 浅拷贝
浅拷贝:只拷贝值类型,引用类型还是指向地址
var obj1 = {
name:"liang",
age:18,
friend:["小红","小绿",{
name:"小蓝",job:"teacher"
}]
}
- 01 ES6 扩展
var obj2 = {
...obj1
}
//浅拷贝(如果属性值是引用类型,两个变量的属性都指向同一个内存地址)
- 02 循环
var obj3 = {};
for(var k in obj1){
obj3[k] = obj1[k];
}
- 03 Object.assign 把参数的两个对象进行合并,属性相同后面覆盖前面
var obj4 = Object.assign(obj1,{})
- 控制台操作
3.2深拷贝
深拷贝:目标对象与源对象相互之间切断联系
var obj1 = {
name:"xiaoyuan",
age:18,
friend:["小红","小绿",{
name:"小蓝",
job:"teacher"
}],
say(){
alert("你好我是" + this.name)
}
}
- JSON 转字符,字符串转对象
var obj2 = JSON.parse(JSON.stringify(obj1))
//JSON字符串的值只会保留 数组,对象,字符串,数字,null,undefined
//对象中的函数会被过滤掉( 一般数据也是不带函数的)
3.2.2先介绍一下递归的概念
递归:递归就是函数自己调用自己,要有结束条件
//需求:计算从1+2+..+5;
function add(n){
if(n == 1){ //如果n是1,直接返回(递归函数的结束条件)
return 1
};
return n + add(n - 1)
}
alert(add(5)) //15
//分析
/*
当n=1时,add(1) ==> 返回1
当n=2时,add(2) ==> 返回2+add(1) ==>2+1
当n=3时,add(3) ==> 返回3+add(2) ==> 3+2+1
当n=4时,add(4) ==> 返回4+add(3) ==> 4+3+2+1
当n=5时,add(5) ==> 返回5+add(4) ==> 5+4+3+2+1
*/
- 斐波那契数列
求斐波那契 数为n的数列
- 下一个数等于前面两个数之和,第0位和第1位返回的是1
- 1,1,2,3,5,8,13,21,34,55,89,…
function fib(n){
if(n == 0 || n == 1){ //如果是第0位返回1,如果是第1位返回1
return 1
}else{
return fib(n - 1) + fib(n - 2) //如果其他位 返回 前面两个数的和
}
}
// alert(fib(41));此种方法计算有局限,最高计算到41位
//分析
/*
n=0时,fib(0)==> return 1
n=1时,fib(1)==> return 1
n=2时,fib(2)==> return fib(1)+fib(0)==>1+1=2
n=3时,fib(3)==> return fib(2)+fib(1)==>2+1=3
n=4时,fib(4)==> return fib(3)+fib(2)==>3+2=5
*/
- 升级
function getFib(n){
//如果小于1 返回1
if(n <= 1){
return 1
}else{
//做数组arr
var arr = [1,1]
//从第2位开始算
for(var i = 2;i <= n;i++){
arr.push(arr[i - 1] + arr[i - 2])
//第n位数 是前面两个数的和
}
//console.log(arr)
return arr.pop() //返回最后一个(删除并返回最后一个)
}
}
alert(getFib(45))
- 通过判断类型 递归深拷贝
深拷贝:目前对象与源对象相互之间切断联系
递归就是函数调用自己,一定要有结束条件
var obj1 = {
name: 'mumu',
age: 18,
friend: ["小红", "小绿", {
name: '小蓝',
job: 'teacher'
}],
say() {
alert("你好我是" + this.name)
}
}
//如果是数组需要递归,如果是对象需要递归,如果是值类型递归结束
function deepCopy(obj) {
//typeof 是object 不是null
if (typeof obj == "object" && obj != null) {
//引用类型
var temp = null;
//如果是数组
if (obj instanceof Array) {
//要返回的结果应该是个数组
temp = [];
for (var i = 0; i < obj.length; i++) {
temp = [];
//递归拷贝数组
for (var i = 0; i < obj.length; i++) {
//temp[i]=obj[i] //**这样就是普通浅拷贝
temp[i] = deepCopy(obj[i]); //递归拷贝
//函数自己调用自己 返回的 拷贝 obj[i]的结果
}
} else {
//返回的结果就是对象
temp = {};
//递归拷贝对象
for (var k in obj) {
temp[k] = deepCopy(obj[k]);
}
}
//返回的结果
return temp;
} else {
//非引用类型或者其他
return obj
}
}
var obj2 = deepCopy(obj1);
// var obj1 = {
// name: 'mumu',
// age: 18,
// friend: ["小红", "小绿", {
// name: '小蓝',
// job: 'teacher'
// }],
// say() {
// alert("你好我是" + this.name)
// }
// }
}
//01 进入deepCody 函数
// typeof obj1 结果就是object 并且不是null
// 02 obj1 instanceof Array 结果是 false
// temp ={}
// for (var k in obj1){
// temp[k] = deepCopy(obj1[k]);
// }
//03 return temp;//返回temp 对象(拷贝好的对象)
// ====================================2.1
// temp[name] = deepCopy(obj1["name"]) deepCopy("mumu")
// typeof "mumu" 不是object 也不是null return "mumu"
// temp[name] = "name"
// ===================================2.2
// temp[age]=deepCopy(obj1["age"]) deepCopy(18)
// typeof 18 不是object 也不是 null return 18
// temp[age] = 18
// ==================================== 2.2
// temp["friend"] =deepCopy(obj1["friend"])
// ===========================================2.2.1
// typeof ["小红","小绿",{name:"小蓝",job:"teacher"}] 结果是object 也是null
// ["小红","小绿",{name:"小蓝",job:"teacher"}] instanceof 结果为true
// temp["friend"] =
// for(var i = 0;i<["小红","小绿",{name:"小蓝",job:"teacher"}].length;i++ ){}
//----------------------------只要是引用类型就递归拷贝下去,直到拷贝typeof 不会object
04 数据类型转换
4.1 隐式转换
数据类型转换:强制转换,隐式转换
强制转换:
- Number()转换为数字,
- String() 转换为字符串
- Boolean() 转换为布尔值
隐式转符号:
- +字符串连接符号(隐式转换为字符串)
- 数学运算:+ - * / (隐式转换为数字)
- >< >= <= || && ! 比较与逻辑(隐式转换为布尔值)
- +字符串连接符号 会尝试把其他类型转换为 字符串
//字符串和任意数据 + 连接 都会转换为字符串
var a = "100";
var b = 10;
var c = a + b;
alert(c);//10010
-
数学运算符号会尝试隐式 转换为数字
如果数据没有转换数字成功,结果就是NaN
true 转换为数字,默认转换为1;false默认会转换为0
//+ - * / == 会尝试把其他类型转换为数字
//转换失败 NaN
//false 转换为 0
//true 转换 1
var a = "10";
var b = 2;
var c = a * b;
console.log(c);//20
var a = "abc10";
var b = 2;
var c = a * b;
console.log(c);//NaN
var a = true;
var b = 2;
var c2 = a * b;
console.log(c2) //2
var a = false;
var b = 2;
var c3 = a * b;
console.log(c3) //0
- 逻辑与比较运算符号,会把变量转换为布尔值
空字符串,0,null,undefined,NaN,false 才会被转换为false,其他都会被转换为true;
把以上的变量值称为falsely变量值,其他的变量都称为truely变量值
//><>= <= ! != == 判断与逻辑返回会尝试把其他类型转换布尔值
//falsely变量转换为false
// "" null NaN undefined 0 转换为false
var a = "";
console.log(!!a); //false
console.log(!!-1); //true
console.log(!!NaN) //false
4.2 严格等于
== (等于) 判断隐式转换后值是否相等
=== (严格等于) 判断类型与值是否相对
- ==
console.log(100 == "100"); //true(如果两边类似数字会优先隐式转换为数字)
console.log("" == false); //true
console.log(1 == true);// true
console.log(null == undefined);// true 隐式转换后都为fasle
console.log(null == null); // true 空指针都指向一个地方(空)
console.log(undefined == undefined);//
console.log(null == NaN);false(特殊) 数字不等于空
console.log([] == []);//false //两块不同的内存地址
console.log({}== {});//false //两块不同的内存地址
- ===
// === 严格等于 判断类型 与值 是否相对
alert(0 === ""); //false
alert([] === []); //false
alert({} === {}); //false
alert(null === undefined); //false
alert(null === null); //true
//应该都用=== 严格等于
//判断是为null 还是undefined 可以用===
- 什么时候用=== 什么时候用==
答:应该在任何时候都使用=== (当判断是否为null或者为undefined可以为特例)
05 if与逻辑运算符
5.1 if
- if判断只要求()内 表达式结果是否为truely变量
- falsely变量:false “ ” 0 NaN false undefined null;对变量取两次反; !! 结果为false的变量,称为falsely变量
- 除了falsely变量外其他都是truely变量
var a = 10;
if(a){
console.log("条件通过") //条件通过
}
//条件通过
var b = "";
if(b){
console.log("b通过")
}else{
console.log("b不通过")
}
// b不通过
5.2 逻辑 或
A||B 或
A为真(truely) 结果为A ,否则结果为B
- 如果前面的变量为truely,最终的结果为第一个,如果为falsely,结果为第二个
var a = 15 || 0;
var b = false || 50;
var c = false || undefined;
var d = 0 || false;
console.log(a); //15 15 转换结果为true ,a的值就是15
console.log(b); //50 false 转换的结果为false b的值是50
console.log(c); // undefined
console.log(d); //false
- 举个例子
//例子 num 取不到,或者为0, 或者为null ....最终结果为5
var re = localStorage.getItem("num") || 5;
5.3 逻辑 且
A&&B 逻辑且
A为假(falsely) 结果为A,否则结果为B
- 如果前面的变量为falsely,直接返回 前面的值;如果前面的变量是truely,则返回后面的值
- 判断对象
if(a&&a.b&&a.b.c){}
if(a?.b?.c){}
如果有a并且有a.b并且有a.b.c
06 原型与原型链
6.1 什么是类,什么是实例
什么是类,什么是实例?
- 类:创造对象实例的模板(本质上都是函数) ;如:Array 、Object 、String…
- 构造函数 用new 来执行的函数
- class xxx{}
- 实例:就是由类创建的对象(本质上讲就是对象);如[1,2,3]{name:“mumu”},“abc”
6.2 显示原型与隐式原型
显示原型
- 类/构造函数都有一个显示原型prototype(本质就是对象)
隐式原型
- 每个实例都有一个隐式原型 __ proto __
显示原型与隐式原型的关系
类显示原型prototype等于其创建的实例的隐式原型 __ proto __
var arr = []; arr.__proto__ === Array.prototype;
6.3 原型链
原型链
- 查找对象实例的方法和属性时,先在自身找,找不到沿着__ proto __ 向上查找,__ proto __ 形成的链条关系,我们称为原型链 (实现了js继承)
原型与原型链的作用
- 实现了js 的继承
- 实现了实例的公用属性和方法
- 创建2个类解释原型与原型链(People,Student)
//01 创建people类
function People(name, age) {
this.name = name;
this.age = age;
}
//02 给people 显示原型添加eat方法
People.prototype.eat = function() {
console.log(this.name + "正在吃饭");
}
//03 创建 学生类继承People 类
function Student(name, age, no) {
//执行people构造函数(执行people 函数并把当前的this传入函数,当做people的this)
People.call(this, name, age);
//定义学号
this.no = no;
}
//04 让Student 显示原型链继承 People 的原型链
Student.prototype = Object.create(People.prototype);
//05 修正Student 显示原型链 上的构造函数
Student.prototype.constructor = Student;
//06 在Student 显示原型链添加方法
Student.prototype.study = function() {
console.log(this.name + "正在好好学习");
}
//07 构建 Student 的实例s1
var s1 = new Student("菜菜", 18, 9527);
- 图示
6.4 js实现继承
- class 的extends方法
class Student extends People{
constructor(name,age,no){
//类中继承构造函数
super(name,age)
....
}
}
- 使用原型链
- Student构造函数中继承People
function Student(name,age,no){
People.call(this,name,age)
....
}
- 继承原型链
Student.prototype = Object.create(People.prototype)
- 修正Student构造函数
Stuent.prototype.constructor = Student
- 图示
6.5 实例公用方法(扩展)
怎么样让所有的数组求最大最小值?(通用方法)
- 数组Array 的实例都拥有最大最小值方法
- 把自定义方法挂载到类的原型上
- 💛 可以在所有的数字实例上访问max(min)方法
- 求最大值
Array.prototype.max = function(){
return Math.max(...this);//this就是当前数组,展开求最大值
}
- 求最小值
Array.prototype.min = function(){
return Math.min(...this);//this就是当前数组,展开求最小值
}
- 怎么样让字符串有通用翻转(reverse)方法
String.prototype.reverse = function(){
return this.split("").reverse().join("");
}
准则1:不要需要js默认对象原型上的方法
准则2:不要在js默认对象的原型上添加方法
vue2 数组的双向绑定劫持就是重写了数组的原型上的方法实现的
更多面试题后续更新…