基础js内容

常问基础js

1.1 基本的数据类型介绍,及值类型和引用类型的理解
值类型(基本类型)
字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型:对象(Object)、数组(Array)、函数(Function)。

注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。
值类型

let a = 100;
let b = a;
a = 200;
console.log(b); // 100

值类型是直接存储在**栈(stack)**中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;

引用类型

let a = { age: 20 };
let b = a;
b.age = 30;
console.log(a.age); // 30

引用类型存储在**堆(heap)**中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;

1.2 数据类型的判断

typeof 所有值类型判断 但是 null、对象、数组无法判断
instanceof 原型链逐级属性判断 区分自定义对象类型
object.prototype.toString.call() 原始数据类型判断 弥补typeof无法判断的内容 比较全

  • typeof:能判断所有值类型,函数。不可对 null、对象、数组进行精确判断,因为都返回 object
console.log(typeof undefined); // undefined
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof "str"); // string
console.log(typeof Symbol("foo")); // symbol
console.log(typeof 2172141653n); // bigint
console.log(typeof function () {}); // function
// 不能判别
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof null); // object


  • instanceof:能判断对象类型,不能判断基本数据类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。比如考虑以下代码:
class People {}
class Student extends People {}
const vortesnail = new Student();
console.log(vortesnail instanceof People); // true
console.log(vortesnail instanceof Student); // true
  • instanceof 其实现就是顺着原型链去找,如果能找到对应的 Xxxxx.prototype 即为 true 。比如这里的vortesnail 作为实例,顺着原型链能找到 Student.prototype 及 People.prototype ,所以都为 true 。
  • hasOwnProperty 查找属性 检查的就是 object 自身的对象,并不会检查原型链
  • Object.prototype.toString.call():所有原始数据类型都是能判断的,还有 Error 对象,Date 对象等。
Object.prototype.toString.call(2); // "[object Number]"
Object.prototype.toString.call(""); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(Math); // "[object Math]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(function () {}); // "[object Function]"

那为什么不直接用obj.toString()呢?

需要调用的toString是Object的原型方法,
而Array 、Function等类型作为Object的实例,都重写了toString方法。
根据原型链的知识,优先调用的是重写之后的toString方法
所以采用obj.toString()不能得到其对象类型,应该调用Object上原型toString方法。

为什么要加prototype呢
Object对象和它的原型链上各自有一个toString()方法,第一个返回的是一个函数,第二个返回的是值类型。

// Array  String ... 都是构造函数
 Object.toString.call(Array) //'function Array() { [native code] }'
 Object.prototype.toString.call(Array)//'[object Function]'
 Object.prototype.toString.call([1,2,3,4])// '[object Array]'
 Object.toString.call([1,2,3,4])// error VM1284:1 Uncaught TypeError: Function.prototype.toString requires that 'this' be a Function at Array.toString(<anonymous>) at <anonymous>:1:18

判断是否为数组的几种方法

Array.isArray(arr); // true
arr.__proto__ === Array.prototype; // true
arr instanceof Array; // true
Object.prototype.toString.call(arr); // "[object Array]"

ES6 新属性

Set: ES6 提供了新的数据结构。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Map: ES6 提供了新的数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

0.1+0.2 !== 0.3

进制转换 :js 在做数字计算的时候,0.1 和 0.2 都会被转成二进制后无限循环 ,但是 js 采用的 IEEE 754 二进制浮点运算,最大可以存储 53 位有效数字,于是大于 53 位后面的会全部截掉,将导致精度丢失。。

  • Number.EPSILON实际上是ES6 新增的 能够表示JavaScript 的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了。一般来说为 Math.pow(2, -52) ,
function isEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}
console.log(isEqual(0.1 + 0.2, 0.3)); // true

原型链

在这里插入图片描述

函数this指向

由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。

https://www.ruanyifeng.com/blog/2018/06/javascript-this.html

作用域

https://juejin.cn/post/6844903797135769614

for (let i = 0; i < 10; i++) {
  // ...
}
console.log(i);
// ReferenceError: i is not defined
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,

// let
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

//闭包
var a = [];
for (var i = 0; i < 10; i++) {
  (function(i){a[i] = function () {
    console.log(i);
  };})(i)
}
a[6](); // 6

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。
变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,

另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
如果没有重新定义j 则循环体内部可以得到父级的继承值 j 在这里插入图片描述

如果重新定义 j 则循环体内部报错 在初始化 j 之前不能使用 j 在这里插入图片描述
直接改写 j 打印出 j 重新定义的内容
在这里插入图片描述
执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。

手动实现call

call: 手动给 obj 增加了一个 fn 属性,我们执行完再使用对象属性的删除方法(delete)删除fn

Function.prototype.myCall = function (context) {
  // 判断调用对象 this目前就是fn
  if (typeof this !== "function") {
    throw new Error("Type error");
  }
  // 获取非context的其他参数 移植到新函数中
  let args = [...arguments].slice(1);
  let result = null;
  // 判断 context 是否传入,如果没有传就设置为 window
  context = context || window;
  // 在context上添加函数的this
  // this 即为我们要调用的方法
  context.fn = this;
  // 执行要被调用的方法
  result = context.fn(...args);
  // 删除手动增加的属性方法
  delete context.fn;
  // 将执行结果返回
  return result;
};
var obj = {
  value: "test",
  name:'cld'
};

function fn() {
  console.log(this.value);
}
 fn.myCall(obj)

打印 this与context

![在这里插入图片描述](https://img-blog.csdnimg.cn/25cf55a908394169b177f76bd551be2d.png

call,apply,bind的基本介绍

改变函数执行时的this指向,目前所有关于它们的运用,都是基于这一点来进行的

语法

  • fun.call(thisArg, param1, param2, …)
  • fun.apply(thisArg, [param1,param2,…])
  • fun.bind(thisArg, param1, param2, …)

调用call/apply/bind的必须是个函数
call、apply和bind是挂在Function对象上的三个方法,只有函数才有这些方法。
只要是函数就可以,比如:
Object.prototype.toString就是个判断类型函数 Object.prototype.toString.call(data)

  • apply是以a开头,它传给fun的参数是Array,也是以a开头的。
  • call/apply改变了函数的this上下文后马上执行该函数
  • bind则是返回改变了上下文后的函数,不执行该函数

防抖节流

防抖
将多次操作合并为一次操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
缺点:如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟


//防抖debounce代码:
function debounce(fn,delay) {
    var timeout = null; // 创建一个标记用来存放定时器的返回值
    return function (e) {
        // 每当用户输入的时候把前一个 setTimeout clear 掉
        clearTimeout(timeout); 
        // 然后又创建一个新的 setTimeout, 这样就能保证interval 间隔内如果时间持续触发,就不会执行 fn 函数
        timeout = setTimeout(() => {
            fn.apply(this, arguments);
        }, delay);
    };
}
// 处理函数
function handle() {
    console.log('防抖:', Math.random());
}
window.addEventListener('scroll', debounce(handle,500));

函数节流(throttle)
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,
实现方式:每次触发事件时,如果当前有等待执行的延时函数,则直接return不执行


//节流throttle代码:
function throttle(fn,delay) {
    let canRun = true; // 通过闭包保存一个标记
    return function () {
         // 在函数开头判断标记是否为true,不为true则return
        if (!canRun) return;
         // 立即设置为false
        canRun = false;
        // 将外部传入的函数的执行放在setTimeout中
        setTimeout(() => { 
        // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。
        // 当定时器没有执行的时候标记永远是false,在开头被return掉
            fn.apply(this, arguments);
            canRun = true;
        }, delay);
    };
}
 
function sayHi(e) {
    console.log('节流:', e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi,500));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Embrace924

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值