这里补充一个小小的知识点:js中数据类型分为基本数据类型
和引用数据类型
。
- 基本数据类型 :Number,String,Boolean,null,undefined,Symbol(ES6新增,表示
独一无二的值
),Bigint(比Number数据类型支持的范围更大
的整数值,整数溢出不是问题) - 引用数据类型:Object,Array,Function,Date
typeof能判断哪些类型
- 能识别所有的值类型(输出类型字符串如:"string")
- 识别函数(输出'function')
- 判断是否是引用类型(只输出object,不可再细分)
csharp
typeof null //'object'
typeof ['a','b'] //'object'
typeof {x:100} //'object'
typeof
可以区分除了Null
类型以外的其他基本数据类型,以及从对象类型中识别出函数(function)。
其返回值有:number
、string
、boolean
、undefined
、symbol
、bigint
、function
、object
。
其中, typeof null
返回 "object"
如果要识别null
,可直接使用===
全等运算符来判断。
javascript
typeof 1 // 'number'
typeof '1' // 'string'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'
- instanceof
instanceof
一般是用来判断引用数据类型,但不能正确判断基本数据类型,根据在原型链中查找判断当前数据的原型对象是否存在返回布尔类型。
javascript
1 instanceof Number; // false
true instanceof Boolean; // false
'str' instanceof String; // false
[] instanceof Array; // true
function(){} instanceof Function; // true
{} instanceof Object; // true
let date = new Date();
date instance of Date; // true
- Object.prototype.toString
javascript
Object.prototype.toString({}) // "[object Object]"
Object.prototype.toString.call({}) // 同上结果,加上call也ok
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(function () {}) // "[object Function]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(document) //"[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"
- Array.isArray
Array.isArray(value)
可以判断 value 是否为数组。
javascript
Array.isArray([]); // true
Array.isArray({}); // false
Array.isArray(1); // false
Array.isArray('string'); // false
Array.isArray(true); // false
常见值类型:
- undefined
- String字符串
- Number
- Boolean
- Symbol(ES6)新增
常见引用类型:
-
对象Object
-
Array数组
-
null (特殊引用类型,指针指向为空地址)
-
function 函数(特殊引用类型,但不用于存储数据)
值类型和引用类型的区别
手写深拷贝
深拷贝 :创建一个新的对象
来承接原对象
中的原始值
,拷贝得到的对象不会
受原对象的影响。
javascript
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 不是原型的属性,hasOwnProperty判断自身是否有指定的属性,因为for...in会遍历原型上的属性
这行代码的作用是确保在进行深度克隆时,只复制对象自身拥有的属性,而不会复制从原型链继承而来的属性。
if (obj.hasOwnProperty(key)) {
// 递归
result[key] = deepClone(obj[key]);
}
}
// 返回结果
return result;
}
4. 三种发生类型转换的情况
-
字符串拼接
-
==运算符,==会尝试类型转换之后再判断相等
-
if判断语句,实际是执行两次非运算判断是否是turly变量
以下是falsely变量(除此之外都是 truly 变量):
字符串拼接
ini
const a =100+'10' //'10010'
const b =true+'10' // 'true10'
何时使用===?何时使用==?
在JavaScript中,===
和 ==
是用于比较两个值的运算符。
-
===
(严格相等):===
用于严格比较两个值,包括它们的类型和值。- 只有当两个值的类型和值完全相同时,
===
才会返回true
。 - 例如:
5 === 5
会返回true
,但是5 === '5'
会返回false
,因为它们的类型不同。
-
==
(相等):==
也用于比较两个值,但它会在必要时进行类型转换,再进行比较。- 这意味着在使用
==
时,如果两个值的类型不同,JavaScript 会尝试将它们转换成相同的类型,然后再比较。 - 例如:
5 == '5'
会返回true
,因为 JavaScript 将字符串'5'
转换成了数字5
,然后进行比较。
建议使用===
的情况:
- 当你想要确保比较的值既具有相同的类型又具有相同的值时,使用
===
是明智的选择。 - 在大多数情况下,推荐使用
===
,因为它避免了类型转换可能导致的意外行为。
建议使用==
的情况:
- 当你明确地想要允许 JavaScript 进行类型转换时,可以使用
==
。这可能在一些特定的情况下会很有用,但要小心,因为它可能会导致一些意外的行为。
总的来说,使用 ===
通常更加安全和可靠,因为它不会进行类型转换,只有在类型和值都相同时才返回 true
。
==运算符
ini
100=='100' //true
0=='' //true
0 ==false //true
false == '' //true
null == undefined //true
ini
//除了 == null 之外,其他都一律用 ===
// eg:
const obj = { x: 100 };
if (obj.a == null) {
}
//相当于
if (obj.a === null || obj.a === undefined) {
}
if语句和逻辑运算
-
if判断语句,实际是执行两次非运算判断是否是turly变量
以下是falsely变量(除此之外都是 truly 变量):
ini!!0 === false !!NaN === false !!'' === false !!null === false !!undefined === false !!false === false
如何判断一个变量是不是数组
a instanceof Array
a.__proto__ === Array.prototype
ini
let arr = [1, 2, 3];
let notArr = 'Hello';
console.log(arr.__proto__ === Array.prototype); // true
console.log(notArr.__proto__ === Array.prototype); // false
Array.isArray()
(推荐)Object.prototype.toString.call() 方法
:
javascript
javascriptCopy code
let arr = [1, 2, 3];
let notArr = 'Hello';
console.log(Object.prototype.toString.call(arr) === '[object Array]'); // true
console.log(Object.prototype.toString.call(notArr) === '[object Array]'); // false
这种方法利用了 Object.prototype.toString
方法返回一个描述了对象类型的字符串,例如 [object Array]
表示数组类型。
class
typescript
// 类
class Student {
constructor(name, number) {
this.name = name
this.number = number
// this.gender = 'male'
}
sayHi() {
console.log(
`姓名 ${this.name} ,学号 ${this.number}`
)
// console.log(
// '姓名 ' + this.name + ' ,学号 ' + this.number
// )
}
// study() {
// }
}
// 通过类 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继承
typescript
// 父类
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()
原型
原型关系
- 每个class都有显示原型 prototype
- 每个实例都有隐式原型 proto
- 实例的_proto_ 指向对应class 的prototype
javascript
// class实际上是函数,可见是语法糖
typeof People //'function'
typeof Student //'function'
// 隐式原型和显示原型
console.log(xialuo._proto_);
console.log(Student.prototype);
console.log(xialuo._proto_ === Student.prototype); //true
原型链
javascript
console.log(Student.prototype._proto_);
console.log(People.prototype);
console.log(People.prototype === Student.prototype._proto_); //true
instanceof本质也是基于原型链
a instanceof B
,本质是如果a沿着原型链能够找到B.prototype
,那么就返回true,否则返回false。因此,以上第一二种方法如果修改了原型链,则判断出错。
手写instanceof
javascript
const instanceOf = (A, B) => {
let p = A;
while (p) {
if (p === B.prototype) {
return true;
}
p = p.__proto__;
}
return false;
}
console.log(instanceOf(1, Number)); // true
console.log(instanceOf([], Array)); // true
console.log(instanceOf([], Object)); // true
class的本质,如何理解
class是一种语法糖
- class是ES6的新特性,用来定义一个类。
- class有构造方法constructor、属性、方法
- 子类可以通过extends继承父类,通过super调用父类构造方法
class本质也是通过原型来实现的
比如定义一个类Person,定义一个类Student继承自Person,通过Student创建一个实例zhangsan。
则zhangsan.__proto__ === Student.prototype
、Student.prototype.__proto__ === Person.prototype
。
有关原型链,可以参考我的另一篇文章:JS原型与原型链
作用域和自由变量
作用域
- 全局作用域
- 函数作用域
- 块级作用域(ES6新增)
自由变量
自由变量的值:现在本级作用域查找,如果未找到,向上级作用域依次查找,直到找到为止,如果到全局作用域都未找到,则报错 xxx is not defined
。
什么是闭包?闭包会用在哪里?
闭包
-
作用域应用的特殊情况,有两种表现:
-
函数作为参数被传递
scssfunction print(fn) { let a = 200; fn(); } let a = 100; function fn() { console.log(a); } print(fn); // 打印100
-
函数作为返回值被返回
inifunction create() { let a = 100; return function() { console.log(a); } } let fn = create(); let a = 200; fn(); // 打印100
闭包
:所有自由变量的查找,是在函数定义的位置向上级作用域查找,不是在函数执行的位置!!
实际开发中闭包的应用场景
- 设置私有变量
- 函数节流、防抖
- 循环中拿到正确的值
- 隐藏数据,做应该简单的cache工具
kotlin
// 闭包隐藏数据,只提供 API
function createCache() {
const data = {} // 闭包中的数据,被隐藏,不被外界访问
return {
set: function (key, val) {
data[key] = val
},
get: function (key) {
return data[key]
}
}
}
// data.b = 200// 不能访问createCache,会出现 Uncaught ReferenceError: data is not defined
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
this 的不同应用场景,如何取值?
this的取值,是在函数执行的时候确定的,不是在函数定义的时候确定的。
- 全局作用域或者普通函数中this指向全局对象window(注意定时器中的this指向window)
- 对象调用方法时,指向调用方法的对象本身
- 构造函数中this指向构造函数实例
- 箭头函数的this指向它声明时作用域的this
- 定时器中的this指向window(但如果定时器回调是使用箭头函数声明,则指向声明时作用域的this)
- 事件绑定方法的回调,this指向绑定对象
- 使用
call、apply、bind
改变this指向,传入什么,this就绑定什么
javascript
const zhangsan = {
name: '张三',
sayHi() {
// this 即为当前对象
console.log(this);
},
wait() {
setTimeout(function () {
// this === window
console.log(this);
})
}
}
箭头函数的this会寻找上级作用域的this的指向
javascript
const zhangsan = {
name: '张三',
sayHi() {
// this 即为当前对象
console.log(this);
},
wait() {
setTimeout(() => {
// this 即为当前对象
console.log(this);
})
}
}
javascript
class People {
constructor(name) {
this.name = name
this.age = 20
}
sayHi() {
console.log(this);
}
}
const zhangsan = new People('张三')
zhangsan.sayHi() //zhangsan对象
9. 手写bind函数
前期知识
javascript
// 模拟 bind
Function.prototype.mybind = function () {
// 将参数解析为数组
const args = Array.from(arguments);
//方法二
//const args = Array.prototype.slice.call(arguments)
// 获取 this(第一个参数,即数组第一项)---取出数组的第一项,数组剩余的就是传递的参数
const t = args.shift();
// 此时this是fn1.bind(...)中的 fn1
const self = this; //当前函数
// 返回一个函数
return function() {
// 执行原函数,并返回结果
return self.apply(t, args)
}
}