JavaScript基础

2023-10-30  本文已影响0人  渚清与沙白

数据类型有 StringNumberBooleanNullUndefinedObject
十六进制:0x 开头
八进制: 0 开头
二进制:0b 开头 Chrome与firefox支持
Infinity: 无穷
typeof:是一个运算符,用于获取数据类型,其结果类型是一个字符串类型
instanceof: 检查一个对象是否是一个类的实例
isNaN():用来判断是否是非数字类型
NaN: undefined and Number,undefined和数字相加结果是 NaN

数据类型强制转换

转换为String
转换为Number

一、使用Number()函数

二、字符串转为Number类型

<script>
  var a = '100px';
  a = parseInt(a,10);// 100 第二个参数10表示10进制
</script>
转换成Boolean类型

运算符

var res = 5 && 6; // 6
res = 0 && 2;// 0
res = 2 && 0;// 0
res = NaN && 0; // NaN
res = 0 && NaN;// 0
比较运算
// 将10转换为了数字类型
console.log('10000' > + '10')  //  true

// js中使用Unicode编码  前缀必须是\u  , 跟上四位编码
console.log("\u0054")

// html中使用Unicode编码  前缀必须是&#  , 跟上四位编码的十进制,最后分号结尾
<h1> 
  &#9760; 
</h1>
var a = 10;
a == 4  // false
"1" == 1  // true
true == "1"  // true
null == 0  // false
undefined == null  // true   undefined衍生自null

NaN == NaN  // false  NaN不和任何值相等,包括他本身。  isNaN() 可以来判断是否是NaN

对象

// 工厂方法创建
function createObj(name){
  var obj = new Object();
  obj.name = name
  return obj;
}
var a = createObj('A');

// 构造函数创建
function Person(name){
  this.name = name;
}
var per = new Person('A');
  1. 通过[]访问属性,[]中可以传递变量。如 var a = 'name'; obj[a] = '你好'
  2. in运算符可以检查一个对象中是否含有指定的属性。如’name‘ in obj,返回true
  3. 对象的属性值可以是任意类型,也可以是函数,函数也可以称为对象的属性,这个函数就是对象的方法
  4. 枚举对象中的属性 for ... in,原型中的属性也会遍历出来
  5. hasOwnProperty() 检查对象自身是否有该属性,原型中的不会查找出来
  6. 查找一个对象的属性,如果没有回返回undefined
函数
function 函数名 ([形参...]){
  语句
}

函数内部可以声明函数,函数没有返回值,获取返回值是undefined

// 匿名函数
(function(){
  console.log('1');
})()

// 传参
(function(x, y){
  console.log(x);
  console.log(y);
})(100, 200)

立即执行函数只会执行一次

fn(); // 函数声明会提升到最前面,这里可以调用函数
function fn(){
  console.log(1);
}

fun(); // var 声明的变量会提升,但不赋值,所以此时无法调用函数
var fun = function(){
  console.log(2);
}
作用域

作用域规定了变量能够被访问的范围。作用域分为全局作用域局部作用域,局部作用域分为函数作用域块作用域

// 函数声明形式创建函数
// 会在所有代码执行之前创建函数,可以在这里之前调用fun函数
function fun(){}

// 函数表达式创建函数
// fun2变量会提前声明,但未赋值,执行到这里才会赋值。所有不能在之前调用fun2函数
var fun2 = function(){}
for(let i = 0; i< 10; i++){
  // 块用域,外部无法访问变量 i
}
for(var i = 0; i< 10; i++){
  // 块用域,外部可以访问变量 i
}
this

解析器在调用函数每次都会向函数内部传递一个隐含的参数this。this指向的是一个对象,这个对象称之为函数执行的上下文对象。根据函数的调用方式不同,this会指向不同的对象。

构造函数

构造函数调用需要使用new函数名首字母大写,这是与普通函数的区别。
使用同一个构造函数创建的对象,称之为一类对象,也将一个构造函数称之为

function MyClass(){
}
function MyClass(name, age){
  this.name = name; // this指向构造函数内创建的对象
  this.age = age;

  // 不推荐
  this.say = function(){} // 存在浪费内存问题,可以使用原型,实例对象都可以调用
}

// 静态属性和静态方法
MyClass.name = '';
MyClass.say = function say(){}

// 访问静态属性和方法
const name = `${MyClass.name}你好`
MyClass.say();

原型 prototype

  1. 创建的每一个函数,解析器都会向函数中添加一个属性prototype。这个属性对应着一个对象,这个对象就是原型对象
  2. 如果函数作为普通函数调用,prototype没有任何作用
  3. 当函数以构造函数调用时,它所创建的对象都会有一个隐含的属性指向该构造函数的原型对象,可以通过proto来访问该属性
  4. 原型对象相当于一个公共的区域,所有的实例都可以访问到这个原型对象,可以将对象中共有的属性和方法统一设置到原型对象中
  5. 当访问对象的一个属性或方法时,会先从对象自身中寻找,再去对象原型中寻找,直到原型的原型对象去找
  6. 原型对象也是对象,它也有原型;Object对象的原型没有原型。
function MyClass(){

}
// 向MyClass的原型中添加属性name
MyClass.prototype.name = 'A';
// 向MyClass的原型中添加方法setName
MyClass.prototype.setName = function setName(name){
  console.log(name);
};

var mc = new MyClass();
console.log( mc.__proto__ == MyClass.prototype ); // true
image.png
function User{
  this.name = 'zs',
  this.age = 19,
}
// 给原型对象追加sex属性
User.prototype.sex = '男';
// 实例对象访问追加的sex属性
const sex = user.sex;
function MyClass(){
}
const res = MyClass.prototype.constructor === MyClass; // true

// 覆盖原有的原型对象,会失去constructor
MyClass.prototype = {
  constructor: MyClass, // constructor指回构造函数,否则prototype就失去了constructor
  function fn(){},
  function fun(){},
  function func(){},
}
对象原型

对象原型是实例对象身上的属性,这是一个只读属性。
对象原型中也存在一个constructor,指向创建实例对象的构造函数
对象原型指向该构造函数的原型对象

构造函数、原型对象和对象原型的关系.jpg
function MyClass(){
}
const cls = new MyClass();
const res = MyClass.prototype === cls.__proto__; // true
 
原型链

原型链其实就是一个查找规则。使用一些属性和方法时,去查找这些属性和方法的规则。

原型链.jpg
原型继承

通过原型对象来实现继承

// 父级对象
function Parent() {
  this.a = 1;
  this.b = 2;
}
// 子级对象
function Child(){ 
}
// 通过Child的原型对象来继承Parent对象
Child.prototype = new Parent();
// 指回原来的构造函数
Child.prototype.constructor = Child;
数组

数组是一个对象,typeof的结果是object。
读取不存在的索引,不会报错,而是返回undefined

可以通过修改 length属性来删除一些元素,数组元素可以是任意类型的数据,包括函数、对象、数组。

回调函数需要定义2个形参,第一个形参肯定在第二个形参的前边
如果返回一个大于0的值,元素会交换位置
如果返回一个小于0的值。则元素位置不变
如果返回一个0,则认为两个元素相等,不交换位置
升序:a - b
降序: b - a
// 排序
aar.sort((a, b)=>{
  return a - b;  // 升序
})
函数对象的方法

需要通过函数对象来调用,调用call()apply()都会调用函数执行。
在调用这两个方法可以将一个对象指定为第一个参数,函数中的this就是传递的这个参数

  1. call(this指向的对象, 参数) 用于调用函数,可以改变this的指向
  2. apply(this指向的对象, 数组参数) 与call一样,区别在于传参必须是一个数组
  3. bind(this指向的对象, 参数) 语法与call相似,可以改变this指向,区别在于不会调用函数
<script>
  function fun(){
    alert(this); // this就是obj,如果没有传递obj ,this则是window
  }
  var obj = {};
  // 使用 call 调用 fun 函数
  fun.call(obj);
   // 使用 apply 调用 fun 函数
  fun.apply(obj);

  // call 和 apply的方式调用 fun函数 并传参
  function fun(a, b){
    console.log(a, b) // 2, 3
  }
  var obj = {};
  fun.call(obj, 2, 3);  
  fun.apply(obj, [2, 3]); // 以数组形式传参
  
  // apply使用场景 求数组最大值
  const max = Math.max(1, 2, 3);
  const max = Math.max(...arr); // 展开运算符
  const max = Math.max.apply(null, [1,2,3]); // this 指向 null
  const max = Math.max.apply(Math, [1,2,3]); // this 指向 Math

  // 3 bind() 不会调用函数,但可以改变this指向,返回值是一个新的函数
  const f = fun.bind(obj); // 会拷贝一份fun 函数,但是this指向了obj
  f(); // 调用新函数
  
  // bind() 使用场景
  btn.addEventListener('click',function(){
    this.disabled = true; // this指向 btn
    setTimeout(function(){
      this.disabled = false; 
    }.bind(this), 2000); // this指向 btn
  });
  // 箭头函数
  btn.addEventListener('click',function(){
    this.disabled = true; // this指向 btn
    setTimeout(()=>{
      this.disabled = false; // this指向 btn
    }, 2000); 
  });

</script>
Date对象
<script>
   // 当前时间
  var d = new Date();

  //创建指定时间
  var d2 = new Data("12/21/2023 14:00:00");
  // 获取一个月中的第几天 1 - 31
  var date = d2.getDate();
  // 获取一周中的第几天  0 - 6
  var day = d2.getDay();
  // 获取月份  0 - 11
  var month = d2.getMonth();
  // 获取年份
  var year = d2.getFullYear();
  // 获取小时 0 - 23
  var hour = d2.getHours();
  // 获取分钟 0 - 59
  var minute = d2.getMinutes();
  // 获取秒 0 - 59
  var second = d2.getSeconds();
  // 获取毫秒数  0 - 999
  var mill = d2.getMilliseconds();
  // 获取当前时间对象的时间戳
  var time = d2.getTime();
  // 获取当前时间戳
  time = Date.now();
  
</script>
包装类

JS提供了三个包装类可以将基本数据类型转换为对象

var num = 123;
// 先将值使用包装类转换为对象,再调用对象的toString()方法
num = num.toString(); 
var type = typeof num; // String
var str = "name";
// 字符串长度
str.length
// 根据索引获取指定字符
str.charAt(0)
// 根据索引获取指定字符的Unicode编码
str.charCodeAt(0) 
// 根据字符编码获取字符
String.fromCharCode(72)  // H
// 连接多个字符串
str.concat("张","三")
// 检索字符串是否含有指定内容,返回第一次出现的索引位置。第二个参数是指定查找的开始位置
str.indexOf('e',1); // 3 
// 从末尾开始查找  返回的index是从前开始数,第二个参数指定从前开始查找,找到指定位置结束
str.lastIndexOf(’e‘,3)
// 截取指定范围的字符串  参数可以为负数
str.slice(0,2)
// 截取字符串  参数不可为负数
str.substring(0, 2)
// 截取字符串  1参:开始位置   2参:长度
str.substr(0, 2)
// 拆分字符串
str.split()
// 转小写  不影响源数据
str.toLowerCase()
// 转大写  不影响源数据
str.toUpperCase()
正则表达式
// 函数形式创建
/*
*语法:
*  RegExp(’正则表达式‘, '匹配模式')
*  匹配模式:
*     i: 忽略大小写
*     g: 全局匹配
*      可以为一个正则设置多种匹配模式  顺序无关   /a/ig 或 /a/gi
*/
var reg = new RegExp('a','i');

/*
* 字面量创建
*  语法:
*    /正则表达式/匹配模式
*/
reg = /a/i; // 匹配a 忽略大小写

// 或:|
reg = /a|b|c/; // 匹配a或b或c
/*
*  或:[]
*  [a-z]  任意小写字母
*  [A-Z] 任意大写字母
*  [A-z]  任意字母
*  [0-9]  任意数字
*/
reg = /[a-z]/
// 检查是否含有 abc 或 adc 或 aec
reg = /a[bde]c/

/*
* [^ ] 排除
*/
reg = /[^ab]/; // 除了a, b都满足

/*
*  量词:通过量词设置一个内容出现的次数
*  只对前面的一个内容起作用
*  次数:{n}, {m, n},{m,}
*  至少1个:    +   相当于{1,}
*  0个或多个:   *   相当于{0,}
*  0个或1个:    ?   相当于{0,1}
*/
reg = /a{3}/; // 匹配连续出现3次的a
reg = /(ab){3}/; // 匹配连续出现3次的ab
reg = /ab{1,3}c/; // 出现1 到 3次 的b
reg = /ab{3,}c/; // 3次以上
reg = /ab+c/; // 至少一个b
reg = /ab*c/; // 0个或多个b

/*
*  检查一个字符是否以 xx 开头
*  ^  表示开头
*  $  表示结尾
*/
reg = /^a/; //  匹配开头的a
reg = /a$/; //  匹配结尾的a
// 如果同时使用 ^和$,则要求字符串必须完全符合正则
reg = /^a$/; //  匹配开头的a,同时也是结尾的a
reg = /^a|a$/;  // 以a开头或者以a结尾

//手机号正则
reg = /^1[3-9][0-9]{9}$/;

/*
*  .  表示任意字符
*  \  转义字符
*  \\  表示 \
*  注意:使用构造函数创建正则时,他的参数是一个字符串,而\是字符串中的转义字符,
*     如果使用\则需要使用\\来代替
*/
reg = /./;  //  匹配任意字符
reg = /\./;  //  匹配 .
reg = new RegExp("\\.");  //  需要写2个\

/*
*  \w  任意字母、数字、_  [A-z0-9_]
*  \W  除了字母、数字、_  [^A-z0-9_]
*  \d  任意的数字  [0-9]
*  \D  除了数字  [^0-9]
*  \s  空格
*  \S  除了空格
*  \b  单词边界
*  \B  除了单词边界
*/
// 检查是否有child单词
reg = /\bchild\b/;
str = str.replace(/^\s*/,"");  //  去除开头的空格
str = str.replace(/\s*$/,"");  //  去除末尾的空格
str = str.replace(/^\s*|\s*$/g,"");  //  去除开头或结尾的空格

/*
*  电子邮件
*    hello            .  world            @  abc         .  com.cn
*    任意字母数字下划线  .  任意字母数字下划线  @  任意字母数字  .  任意字母
*    \w{3,}    (\.\w+)*  @  [A-z0-9]+  (\.[A-z]{2,5}){1,2}
*/
reg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/;

垃圾回收机制

全局变量在页面关闭时回收,局部变量使用完毕自动回收

闭包

闭包 = 内层函数 + 外层函数的变量

// 1. 基本形式

// 外层函数
function fun(){
  let a = 1; // 外层函数的变量
  // 内层函数
  function fun2(){
    console.log(a); // 内层函数使用了外部函数的变量
  }
  fun2(); // 必须要调用,外部无法访问 fun2 函数
}
fun(); // 必须要调用

// 2. 常见形式
function fun(){
  let a = 1; 
  // 内层函数
  function fun2(){
    console.log(a); 
  }
 return fun2; // 返回一个函数
}
const fn = fun(); // 接收这个函数
fn(); // 调用这个函数

作用:支持外部访问函数内部的变量
应用:实现数据的私有
闭包中外层函数的变量不会被回收,应该被回收但是没有被回收,闭包存在内存泄漏的风险。

函数参数

函数参数分为动态参数和剩余参数

动态参数

arguments

arguments参数仅仅存在于函数中。
调用函数时,浏览器都会传递两个隐含的参数,一个是this,一个是arguments
arguments是一个类数组对象,但并不是数组,用来封装实参。
Array.isArray(arguments) : false
arguments instanceof Array : false
在调用函数传递的实参都会在arguments中保存
arguments的length就是实参的长度,通过下标索引来获取实参
不定义形参,也可以通过arguments来获取实参
属性callee对应一个函数对象,就是当前正在执行的函数的对象

<script>
  function fun(a, b){
    console.log(arguments instanceof Array);
    console.log(Array.isArray(arguments));
    console.log(arguments[0]);
    console.log(arguments.length);
    console.log(arguments.callee);
  }
</script>
剩余参数

将不定数的参数表示为一个数组
语法符号:...
放于最末函数形参之前,用于获取多余的实参,获取到的实参是个真数组

function fun(a, b, ...num){
  console.log(num); // 真数组
   // 展开运算符
   // 求最值
  const max = Math.max(...num);
  const min = Math.min(...num);
  // 合并数组
  const arr1 = [1,2];
  const arr2 = [3,4];
  const arr = [...arr1, ...arr2];
}

Object常用静态方法

Object.keys():获取对象所有属性名
Object.values():获取对象所有属性值
Object.assign():对象拷贝,应用场景-给对象添加属性

const obj = {name: 'A', age: 10}
// 获取属性名
const arr = Object.keys(obj)

// 获取属性值
const values = Object.values(obj)

// 拷贝对象
const obj2 = {};
Object.assign(obj2, obj);
// 给obj对象添加gender属性
Object.assign(obj, {gender: 1});
深浅拷贝
const obj = {
  name:' ',
  bean: {
    age: 10,
  }
}
const o = {};
Object.assign(o, obj);// 将bean对象的地址拷贝给o对象,两个对象的bean都是同一个对象
o.bean.age = 100; // 修改拷贝后的对象属性值
o.name = 'red';
console.log(obj.bean.age); // 100
console.log(obj.name); // ''
// 1 递归深拷贝
function deepCopy(newObj, oldObj){
  for(let k in oldObj){
    if(oldObj[k] instanceof Array){
      newObj[k] = [];
      deepCopy(newObj[k], oldObj[k]);
    }else if(oldObj[k] instanceof Object){
      newObj[k] = {};
      deepCopy(newObj[k], oldObj[k]);
    }else{
      newObj[k] = oldObj[k];
    }
  }
}

// 2 lodash / cloneDeep
const newObj = _.cloneDeep(oldObj)

// 3 JSON. stringify()
const newObj = JSON.parse(JSON.stringify(oldObj));
异常处理
// 抛出异常
function fn(a){
  if(!a){
    throw new Error('参数有误');
  }
  return a.toString();
}

// 捕获异常
try{
  ...
}catch(e){
   throw new Error('捕获到异常');
}finally{
  // finally 这里一定会执行
}

防抖与节流

防抖:单位时间内触发重新计时,单位时间到了才会执行
节流:单位时间内只执行一次

延迟一段时间再调用函数,底层是利用定时器来实现

// setTimeout定时器实现防抖

function debounce(fn, t){
  let timer = null;
  // 需要返回一个函数,作为事件的处理函数
  return function(){
    if(timer) clearTimeout(timer);
    timer = setTimeout(function(){
      fn();
    }, t)
  }
}

// setTimeout实现节流
function throttle(fn, t){
  let timer = null;
  return function(){
    if(!timer){
      timer = setTimeout(function(){
        fn();
        timer = null; // 在定时器回调函数中是无法清除定时
      }, t);
    }
  }
}
上一篇下一篇

猜你喜欢

热点阅读