1. javascript的数据类型有哪些?
基本数据类型:String、Number、Boolean、Null、Undefined、BigInt、Symbol。
引用数据类型:Object、Array、Function、Date等。
2. 如何判断javascript的数据类型?
- typeof(返回表示数据类型的字符串):只能判断基本数据类型、Function和Object,并且判断Null是Object,Array和Date等也是Object
typeof '' //'string'
typeof null //'object'
typeof [] // ‘object’
typeof function(){} //'function'
- instanceof:判断实例A是不是B构造类型/引用类型,返回一个布尔值
[] instanceof Array //true
let num = 1
num instanceof Number //false
num = new Number(1)
num instanceof Number //true
- Object.prototype.toString.call():所有类型均可判断
Object.prototype.toString.call(123) //'[object Number]'
Object.prototype.toString.call(true) //'[object Boolean]'
Object.prototype.toString.call([]) //'[object Array]'
Object.prototype.toString.call(new Date()) //'[object Date]'
- constructor:根据对象的constructor判断
let arr = [1,2]
let date = new Date()
let f = function(){}
arr.constructor == Array //true
date.constructor == Date //true
f.constructor == Function // true
3. 如何判断两个对象相等,如何判断空对象,如何判断对象有某个属性?
判断对象是否相等:
1、JSON.stringify(obj),将对象转成JSON,之后判断两者是否相等(只适合两个对象的属性顺序是一样的)。
let a = {name:1,age:2}
let b = {name:1,age:2}
JSON.stringify(a) === JSON.stringify(b) //true
2、先判断两个对象的长度是否一致(Obejct.getOwnPropertyNames(a).length),遍历对象判断A中的属性是否在B中也有和判断vlaue是否相等。
//简单写的一个函数
function equals(a,b){
let ao = Object.getOwnPropertyNames(a);
let bo = Object.getOwnPropertyNames(b);
if(ao.length != bo.length) return false;
for(key in a){
if(!b.hasOwnProperty(key)) return false;
if(a[key] != b[key]) return false;
}
return true;
}
let a = {name:1,age:2}
let b = {age:2,name:1}
equals(a,b) //true
判断空对象:
1、转成字符串,再判断是否等于‘{}’。
2、使用Object.keys()返回对象的属性名的数组,判断它的length是否等于0,则为空对象。
3、使用Object.values()返回对象的属性值的数组,判断它的length是否等于0,则为空对象。
4、使用Object.getOwnPropertyNames()方法获取对象的属性名的数组,判断它的length是否等于0,则为空对象。
5、for in循环。
let a = {}
JSON.stringify(a) === '{}' //true
Object.keys(a).length == 0 //true
Object.values(a).length == 0 //true
Object.getOwnPropertyNames(a).length==0. //true
function isEmptyObject(a){
for(key in a){
return false;
}
return true;
}
let a = {}
isEmptyObject(a);
判断对象是否有某个属性:
1、in :如果属性是对象的原型,也会返回true
let obj = {name:'swa'}
'name' in obj //true
'toString' in obj //true
2、Object.hasOwnProperty() :是否具有指定具有自身指定的属性,不是对象原型上的属性。如果对象是用Object.create()创建的话,无法判断。
let a = {name: 'aaa'}
Object.hasOwnProperty('name') //true
Object.hasOwnProperty('toString') //false
let obj = Object.create(null)
obj.p = 2
Object.hasOwnProperty('p') //false
3、Object.prototype.hasOwnProperty.call():可以解决用Object.create()创建对象无法判断的问题。
let a = {name: 'aaa'}
Object.prototype.hasOwnProperty.call(a,'name') //true
Object.prototype.hasOwnProperty.call(a,'toString') //false
let obj = Object.create(null)
obj.p = 2
Object.prototype.hasOwnProperty.call(obj,'p') //true
4、Object.hasOwn() :是ES2022提出的,用于取代Object.prototype.hasOwnProperty()方法。
let a = { name: 'qw'}
Object.hasOwn(a,'name') //true
Object.hasOwn(a,'toString') //false
let obj = Object.create(null)
obj.p = 2
Object.hasOwn(obj,'p') //true
5、Reflect.has() :如果属性是对象的原型,也会返回true。Reflect是一个内置的对象,它提供拦截操作javascript的方法,是ES6为了操作对象而提供新的API。
let a = {name: 'aaa'}
Reflect.has(a,'toString') //true
Reflect.has(a,'name') //true
let obj = Object.create(null)
obj.p = 2
Reflect.has(obj,'p') //true
4. 强制类型转换和隐式类型转换
1、强制类型转换可通过String()、Number()、Boolean()函数
String(123) //'123'
Number('123') //123
Boolean(1) //true
2、隐式类型转换可通过 == 、运算符加减乘除、大于、小于等
- 字符串加数字,数字会变为字符串
- 数字减乘除、大于、小于字符串,字符串都会转为数字
- 规定是undefined == null
undefined == null
null == 0 //false
null == false // false
undefined == 0 //false
'123' == 123 //true 字符串转数字
1 == true //true 布尔转数字
'0' == false // true 两者转数字
'1'+2 //12
'1'-2 //-1
'1'*2 //2
'6'/2 //3
'2'>1 //true
1<'6' //true
5. 创建函数有几种方式?
1、函数声明方式
function f(){}
2、函数表达式方式
var f = function(){}
3、箭头函数
var f =(a,b)=>{
}
6. == 与 ===的区别
1、== 如果两边类型不同,会进行隐式转换再比较
2、=== 是判断两者类型和值是否完全相同
'0' == 0 //true
'0' === 0 //false
undefined === null //false
7. undefined和null的区别
1、定义不同。undefined表示“未定义的值”,当声名了一个变量但没有给它赋值时,该变量的值就是undefined。null表示的是空值,当我们想表示一个变量不包含任何值的时候,可以将它设置为null。
2、用typeof判断数据类型的时候,判断undefined是undefined数据类型,判断null是object数据类型。
3、用Number()强制转换时,undefined是NaN,null是0
4、null是关键字,undefined不是关键字
8.数组降维
1、二维数组降维
- 用concat()方法和扩展运算符
- 用flat(1)
let arr=[1,2,[3,4],5];
[].concat(...arr); //[1,2,3,4,5]
arr.flat(1) //[1,2,3,4,5]
2、多维数组降维:使用flat(Infinity)方法
let arr=[1,2,[3,4],[5,[6,7]],8];
arr.flat(Infinity) // [1, 2, 3, 4, 5, 6, 7, 8]
9.什么是类数组(伪数组),如何转化为真实的数组?
伪数组: 伪数组是一个类似数组的对象,具有类似数组的索引属性和length属性,但是不是真正的数组,可以使用一些数组相关的方法,如for 等,但是一些数组特有的方法如push、pop等则不能直接使用。
常见的伪数组: arguments对象、HTMLColletion对象、NodeList对象。
转换为真实数组的方法:
1、Array.from()
function test(){
return arguments
}
let f = test(1,2)
Array.from(f)) //[1,2]
2、Array.prototype.slice.call()
function test(){
return arguments
}
Array.prototype.slice.call(test(1,2)) //[1,2]
10.如何遍历对象的属性?
1、for in:遍历自身和继承的可枚举属性(不含Symbol属性)
let obj = {name: 'a' , age:10 ,sex: 1}
Object.defineProperty(obj, 'sex', {
enumerable: false,
});
for(key in obj ){
console.log(key) //name age
}
2、Object.keys():返回一个对象自身(不含继承)的可枚举属性(不含Symbol属性)的数组
let obj = {name: 'a' , age:10 ,sex: 1}
Object.defineProperty(obj, 'sex', {
enumerable: false,
});
Object.keys(obj) //'name', 'age']
3、Object.getOwnPropertyNames():返回一个对像所有的属性(包括可枚举和不可枚举属性)的数组(不含Symbol属性)
let obj = {name: 'a' , age:10 ,sex: 1}
Object.defineProperty(obj, 'sex', {
enumerable: false,
});
Object.getOwnPropertyNames(obj) // ['name', 'age', 'sex']
4、Reflect.ownKeys:返回一个自身所有属性名的数组,包括是否枚举和Symbol属性。
let obj = {name: 'a' , age:10 ,sex: 1,[Symbol()]:1}
Object.defineProperty(obj, 'sex', {
enumerable: false,
});
Reflect.ownKeys(obj) //['name', 'age', 'sex', Symbol()]
11.如何给一个按钮绑定两个事件?
使用addEventListener()绑定事件
可以使用removeEventLister()解除事件
var btn = document.getElementById("btn");
btn.addEventListener("click", test1, false); //第三个参数是表示捕获还是冒泡,true:捕获 false:冒泡,默认是冒泡
btn.addEventListener("click", test2, false);
function test1(){
}
function test2(){
}
12.什么是事件冒泡?如何阻止事件冒泡
当点击一个元素触发某种事件时,会由内到外直到父元素上依次触发同类的事件。
使用 event.stopPropagation() 阻止冒泡事件。
不是所有事件都能冒泡,以下事件不能冒泡:blur、focus、load、unload.
13.什么是事件捕获?
当点击一个元素触发某种事件时,会由外到内直到子元素上依次触发同类的事件
可以使用addEventLister(‘click’,function aa(){},true)方法的第三个参数设为true就是捕获
14.如何让事件先冒泡后捕获?
事件默认是先捕获后冒泡
使用事件委托:将单个事件监听器添加到父元素上,以处理子元素上的事件
15.什么是作用域、作用域链?如何延长?什么是暂时性死区?
- 作用域:变量与函数的可访问范围。
作用域分为全局作用域、局部作用域、块级作用域。
全局作用域:在整个文件内起作用,任何位置都可以访问。比如用var声明的全局变量具有全局作用域,全局变量只有关闭浏览器的时候才会销毁,比较占内存资源。
var a = 1;
console.log(a); //1
局部作用域:只能在函数内起作用。局部变量会在程序执行完成的时候立即销毁,节省资源。
function f(){
var a = 1;
console.log(a); //1
}
f();
console.log(a); // 报错Uncaught ReferenceError: a is not defined
块级作用域:任何一对花括号({})中的语句集都属于一个块,在这块区域中定义的变量,只能在这个区域中使用。let、const定义的变量会产生块级作用域
for(let i = 0;i<5;i++){
}
console.log(i);
//Uncaught ReferenceError: i is not defined 报错,只能在{}范围内被访问,外面访问不到
- 作用域链:使用一个变量的时候,js引擎会尝试在当前作用域下查找,如果没找到,会往它的上层作用域查找,依次类推直到找到该变量。
如果找不到该变量会直接报错。
var sex = '女';
function person() {
var name = 'Lily';
function student() {
var age = 18;
console.log(name); // Lily
console.log(sex); // 女
}
student();
console.log(age); // Uncaught ReferenceError: age is not defined
}
person();
- 暂时性死区:针对let、const(块级作用域)这两个关键字而产生的概念,在声明变量之前使用该变量,那么该变量是不可用的,也就被称为暂时性死区。
console.log(a); //Uncaught ReferenceError: a is not defined
let a = 2;
16.变量提升是什么?与函数提升有什么区别?
- 变量提升:是指变量的声明被提前到作用域的顶端。
console.log(a); //undefined
var a = 1;
变量提升只是声明被提升,不包括赋值,等同于下面这段代码:
var a;
console.log(a);
a = 1;
- 函数提升:函数提升的优先级高于变量提升,不会被同名变量声明覆盖,但是会被同名声明赋值后覆盖。
1、函数声明:js在执行之前,会把函数提升在到最前面
f(); //123
function f() {
console.log(123);
}
上面的代码等同于这段代码:
function f() {
console.log(123);
}
f(); //123
2、函数表达式提升:可以理解为一个普通变量的提升
f();
var f = function() {
console.log(123);
}
上面这段代码会报错:Uncaught TypeError: f is not a function,因为js执行之前,f会提升到最前面,值为undefined,不是一个函数,以函数的形式调用会报错。
等同与下面这段代码:
var f;
f();
f = function() {
console.log(123);
}
(练习)函数提升的优先级高于变量提升,不会被同名变量声明覆盖,但是会被同名声明赋值后覆盖。
function a(){}
var a;
console.log(typeof(a)) //function
function a(){}
var a = 1;
console.log(typeof(a)) //Number