文章目录
值类型和引用类型
值类型
- 占用空间小
- 有string、number、boolean、undefine以及es6的symbol
引用类型
- 占用空间大
- 有Object、Array以及特殊引用类型null(特殊引用类型,不用于存储数据function)
- 引用类型使用堆,key指向堆得内存地址
typeof判断类型
- 所有值类型:string、number、boolean、undefine、symbol
- 引用类型将array、object、null都归于object一类,能判断function
const fn = function () {}
const fnType = typeof fn // function
instanceof判断类型
只能用来判断对象、函数和数组,不能用来判断字符串和数字
instanceof只能检测引用类型的,不能检测基本数据类型
a = {} c = [] d = 'abc' e = 123
b = function() {}
a instanceof Object // true
b instanceof Object // true
b instanceof Function // true
c instanceof Array // true
d instanceof String // false
e instanceof Number // false
null instanceof Object // false
深拷贝代码
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) {
if(obj.hasOwnProperty(key)) {
// 递归调用
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
运算符
==运算符
100 = '100' // true
0 = '' // true
0 = false // true
false = '' // true
null = undefined // true
以下是falsely变量,除此之外都是truly变量
!! 0 === false
!! NaN === false
!! '' === false
!! null === false
!! undefined === false
!! false === false
原型相关
// 通过类new 对象/实例
class Student {
constructor(name, number) {
this.name = name
this.number = number
}
sayHi() {
console.log(`姓名${this.name},学号${this.number}`)
}
}
const xiaLuo = new Student('夏洛', 12)
// 父类
class People{
contractor(name) {
this.name = name
}
eat() {
console.log(`吃${this.name}`)
}
}
// 子类
class Student extends People {
contractor(name,number) {
super(name) // 调用父类的contractor(name)
this.number = number
}
sayHi() {
console.log(`姓名${this.name},学号${this.number}`)
}
}
- es6要求,子类构造函数必须执行一次super函数,否则在新建实例时会报错。
- 如果不加super方法,子类就得不到this对象
- 在子类的构造函数中,只有调用了super方法之后,才可以使用this关键字。这是因为子类实例的构建是基于对父类实例的加工,只有super方法才能返回父类的实例
原型关系
- 每个class都有显示原型prototype
- 每个实例都有隐式原型__proto__
- 实例的__proto__指向对应class的prototype
原型的执行规则
- 现在自身的属性和方法寻找
- 如果找不到则自动去__proto__中查找
构造函数、实例、原型对象三者之间的关系
Star.prototype = {
constructor: Star,
sing: function() {
console.log('我会唱歌')
}
movie: function() {
console.log('我会看电影')
}
}
const ldh = new Star()
- 只要是对象就__proto__原型,指向原型对象
- Star原型对象里面的__proto__原型指向的就是Object.prototype
- Object.prototype原型对象里面的__proto__原型,指向为null
原型对象指向问题
- 在构造函数中,里面的this指向的是对象实例 ldh
- 原型对象里面的this指向的是实例对象ldh (函数的调用者)
手写jquery
class jQuery {
contractor(selector) {
const result = document.querySelectorAll(selector)
const length = result.length
for(let i = 0; i < length; i++) {
this[i] = result[i]
}
this.length = length
}
get(index) {
return this[index]
}
each(fn) {
for (let i = 0; i < length; i++) {
const elem = this[i]
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false)
})
}
}
const $p = new jQuery('p')
$p.get(1)
$p.each((elem) => console.log(elem.nodeName))
$p.on('click', ()=> alert('clicked'))
}
class myJquery extends jQuery {
contractor(selector) {
super(selector)
}
// 扩展自己的方法
addClassName(className) {}
}
作用域
this
- 作为普通函数
- 使用call apply bind
- 作为对象方法被调用
- 在class方法中调用
- 箭头函数
this不同引用场景如何取值
// this取值是在函数的时候定义的,不是在函数定义的时候确认的
// bind返回一个函数,需要重新执行函数
function fn1() {
console.log(this)
}
fn1() // window
fn1.call({x: 100}) // {x: 100}
const fn2 = fn1.bind({x: 200})
fn2() // {x: 200}
箭头函数取得是上级作用域的this
call/apply/bind
相同点:三个函数都会改变this的指向
不同点:
- bind会产生新函数
- call和apply不会产生新函数,只是在调用的时候绑定一下
- call和apply的区别,第一个参数都是表示要改变指向的那个对象,apply第二个参数是数组,call是将参数列举出来
手写bind函数
Bind函数创建一个新的函数,在bind被调用时,这个新函数的this被指定为bind的第一个参数,而其余参数将作为新函数的参数,供调用时使用
Function.prototype.bind1 = function() {
// 将参数拆解为数组 Arguments可以获取所有的参数
const args = Array.prototype.slice.call(arguments)
const t = args.shift() // 获取this(数组第一项)
const self = this // fn1.bind(...)中的fn1
return function() { // 返回一个函数
return self.apply(t, args)
}
}
chatGTP
if (!Function.prototype.myBind) {
Function.prototype.myBind = function (context, ...argsPrepend) {
const self = this; // 保存原函数引用
let args = [...argsPrepend]; // 保存预置参数
// 返回一个新的函数,该函数在调用时会设置正确的上下文并传递预置参数
return function (...argsAppend) {
// 构建最终调用原函数时的参数列表,预置参数在前,调用时传入的参数在后
const allArgs = [...args, ...argsAppend];
// 确保在严格模式下也能正确设置this
const fnContext = Object(self);
const boundFn = function () {
return self.apply(this instanceof boundFn ? this : context, allArgs);
};
// 继承原函数的原型
if (typeof self.prototype !== 'undefined') {
boundFn.prototype = Object.create(self.prototype);
}
return boundFn;
};
};
}
// 示例使用
function MyClass() {
this.name = 'Original Class';
}
MyClass.prototype.sayHello = function (firstName, lastName) {
console.log(`Hello ${firstName} ${lastName}, I'm ${this.name}`);
};
const obj = { name: 'Bound Object' };
const boundSayHello = MyClass.prototype.sayHello.myBind(obj, 'John');
boundSayHello('Doe'); // 输出 "Hello John Doe, I'm Bound Object"
异步
- 异步解决单线程等待问题
- 异步应用场景:网络请求,定时任务
闭包
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
// 闭包隐藏数据,只提供api
function createCache() {
const data = {}
return {
set: function(key, val) {
data[key] = val
}
get: function(key) {
return daka[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a')) // 100
闭包:自由变量的查找,是在函数定义的地方向上级作用域查找,不是在执行的地方
function print(fn) {
const a =200
fn()
}
const a = 100
function fn() {
console.log(a)
}
print(fn) // 100