js笔记 遗漏知识的一些补充

本文详细讲解了JavaScript中数组的声明、长度获取与元素添加,argument的用法,函数的两种声明方式、实例化和作用域,对象的声明、构造函数与原型对象,内存分配原理,call和apply的区别,以及事件委派、深拷贝等内容,适合初学者和进阶者阅读。
摘要由CSDN通过智能技术生成

数组

数组的声明

现在声明数组一般不用new

let arr1 = [];
let arr2 = ["s", "a"];

如果直接输出arr,会得到一个对象,里面有长度,和值

数组长度获取

console.log(arr.length);

新增数组元素

在数组已经达到上限的时候,有多种方法可以改变数组长度\

  1. 手动修改arr.length的值,然后再赋值
arr.length++;
arr[arr.length - 1] = 5;
  1. 直接进行赋值,不会有溢出的麻烦
arr[arr.length] = 5;

argument

argument的作用是处理存储所有传递过来的实参,而现在显然可以被rest参数给替代掉
argument是一个伪数组
伪数组

  1. 具有length属性
console.log(argument.length);
  1. 能够通过索引的形式获取argument的值
argument[0]
  1. 不能使用数组的一些方法,如pop, push这也是伪数组与数组的根本区别

两种声明函数的方式

function fun(){};
fun();
var fun = function(){};
fun()

函数的实例化

function fun(
	let a = 5;
	console.log("fsf");
}
var fun1 = fun();

第二种方式是将函数实例化,就像是将这个函数变成了一个构造函数,能够通过点符号获取到这个函数作用域里面的变量和方法,但是不能直接执行这个函数, 所以说构造函数就是没有

作用域

在es6以前js是没有块级作用域的,而是只有局部作用域和全局作用域,局部作用域就是在函数的内部,而块级作用域是在一个{}内部,比如if,for,这些都是没有块级作用域的,内部定义的变量就是全局的

预解析

预解析就是分为变量预解析和函数预解析

变量提升(变量预解析)

变量提升会将变量声明提升到该定义域的最前面,变量赋值不会

console.log(a);
var a = 10;

变量提升后的实际执行顺序为

var a;
console.log(a);
a = 10;

将变量声明提升到定义域最前面

函数提升(函数预解析)

函数提升同理,将函数声明提升到定义域最前面,只需要注意,如果使用var fun = function()的方式定义函数,这算是变量提升,不会把这个函数提升到最前面

fun();
var fun = function(){};

实际执行顺序

var fun;
fun();
fun = function(){};

对象

万物皆对象,对象是无序的属性和方法的集合,有点类似c语言里的结构体的实例化

对象的声明和使用

let student = {
	name:"学生",
	gotoschool: function(){
	}
};
console.log(student.name);
//student["age"]也可以使用name
student.gotoschool();

对象声明的第二种方式new Object();

let student = new Obeject()//创建一个空的对象;
student.name = "学生";
student.gotoschool = function(){};

构造函数

构造函数相当于结构体,就是将对象进行封装;

function Student(name, tel, address, gotoschool){
	this.name = name;
	this.tel = tel;
	this.address = address;
	this.gotoschool = function(){
	};
}
let xiaoming = new Student("xiaoming", 150, "beijing");
console.log(xiaoming.name);
xiaoming.gotoschool();

构造函数的方法不需要形参和实参
构造函数没有返回值
构造函数其实就是一个有了别名的函数,实质上还是一个函数。

遍历对象里的属性和方法

for(let k in student){
	console.log(k);//student, tel , address, gotoschool();
	console.log(student[k]);//xiaoming, 150, beijing;
}

不过我们一般不会去便利方法

内置对象

就是我们能够直接使用的一些简单的方法如Math, Date
Math的一些方法详见 js的一些方法.

Date对象

let date1 = new Date();
//不传入参数返回的是当前的时间
let date2 = new Date(2016, 10, 20);
//传入数字型可能会出现一些问题
let date3 = new Date("2016-10-20 10:10:10");
//字符串型不会出现问题,比较常用

下面是实例化后的一些方法
Data一些方法
获取总的毫秒数,这样做倒计时更加准确

let date = +new Date();//注意这里是+new,而不是new
//如果给Date里加一个时间参数,就会把时间转化成总的毫秒数
console.log(data);//1970年至今的总毫秒数

也可以用最新的h5方法console.log(Data.now())获取当前总的毫秒数

原型对象

现在假设Person是一个构造函数,而p是一个实例对象
p是由Person new出来的一个实例嘛,这个时候有一个属性,prototype,如果我们需要在给所有的实例对象后来加一个属性,比如age,不能在构造函数里加,就可以这样写Person.prototype.age = "20"这个时候,就可以通过p.age访问到.
Person.prototype就是个原型对象,我们可以往prototype里加东西
那p是如何读取到原型对象上的东西的,p有一个属性叫_proto_,p.proto_可以称为隐式的原型对象,而prototype则是显式的原型对象,当我们在使用p.age的时候,先往自己身上找,也就是构造函数里的内容,没有找到age,就开始找_proto,这个属性指向的就是原型对象,Prototype里的东西都在这
原型对象也有原型对象,如果没有特殊更改,原型对象的原型对象就是object,所以p也能访问到Object的属性和方法,比如p.tostring()

不同数据类型的内存分配

分类

js的数组类型分为值类型和引用类型
值类型(也叫简单数据类型)就是string,number, boolean, undefined,null(本质是一个对象类型)
引用类型(复杂数据类型)是需要new来创建,比如数组,date,对象

堆和栈

js里面并没有堆和栈,只是为了方便理解
简单数据类型是存放在栈里面,复杂数组类型是将地址存放在栈里面,然后值放在堆里面,所以是先通过访问栈里面的地址然后再调出值。
所以在函数传参的时候两者就会有区别,简单数据类型直接是复制栈里面的值,复杂数组类型传的是栈里的地址,就像是c++里的引用形参,可以通过修改函数里的数据的值改变外面的值。

call apply

如下的函数

function sss(a, b){
	console.log(this.a,this.b,this.num);
};
let test  = {
	num: 100;
};
sss.call(test, 3, 4);

其实总的来说call只有第一个参数,是一个对象
sss本身是在window下的,所以其中的this也是window,如果想要将其中的this变为test,就是将sss变为test下的,就需要将第一个参数设置为test,后面的参数是函数本身需要的参数


apply与call本身极为相似
只是后面接收参数的方法不一样而已
sss.appy(test, [3, 4]);

throw

throw会停止js然后将throw的值给到catch

let a = 6;
if(a > 5){
    throw "a > 5 ERROR";
}
console.log("未被执行");
try{
    console.log(a);
}catch(err){
    console.log(err);
}

a = 6
在这里插入图片描述
a = 3时
结果

事件委派

当有多个元素都有同样的绑定事件的时候,我们可以将事件绑定给父元素,当子元素触发事件,就会通过冒泡触发父元素的事件,优化性能

深拷贝

  1. 手动递归复制所有属性:
function deepCopy(obj) {
    if (typeof obj !== "object") {
        return obj;
    }
    const newObj = obj instanceof Array ? [] : {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = deepCopy(obj[key]);
        }
    }
    return newObj;
}
  1. 使用 JSON.stringify 和 JSON.parse:
const newObj = JSON.parse(JSON.stringify(obj));

但是这个方法存在一些问题:

  • 无法复制函数
  • 无法复制 undefined 和 symbol
  • 无法解决循环引用的问题
  1. 使用第三方库 lodash 的 cloneDeep 方法:
const newObj = _.cloneDeep(obj);

这个方法是一个非常完全的解决方案,可以深度复制任何对象,但是需要额外依赖第三方库。

数据转成bool

可以用Boolean()显式转换,也可以用!!两个取反转换

一些基础概念

DOM事件流 事件发生时按一定顺序传播,这个过程 document object model
捕获阶段:addEventlistener的第三个参数如果是ture就是捕获阶段,父元素和子元素同时绑定了点击事件,那么会先执行父元素事件,然后再执行子元素
冒泡阶段则相反,addEventlistener默认是冒泡,而有的事件没有冒泡阶段的,如onblur,onfocus

事件对象 添加事件监听时的函数小括号里的加参数,这个参数
addEventListener(“click”,function(ssss){}) sss就包含了click执行时的事件对象信息,有按下时的坐标等。
调用时有兼容性问题,所以就ssss = ssss || window.ssss

事件委托 通过给父元素绑定事件,用event。target来影响子元素,从而提高效率。
子元素没有事件监听,但是它会冒泡,找到父元素,父元素加了事件监听,就会执行事件,通过target找到对应的子元素。

BOM browser object model浏览器对象模型,是与浏览器实现交互的
document是window的子对象,在document里的变量和函数都会变成window的属性和方法,只是我们在调用的时候,可以省略window,如window.alert()

同步任务和异步任务 按照一定顺序的叫同步任务,可以一起执行的叫异步任务。异步任务比如click,timeout,interval
同步任务会被放到主线程栈中一直执行,而异步任务则会被放到任务队列里面,先把所有的同步任务执行完了之后再执行异步任务,而有多个异步任务的时候,会按照顺序先放到异步任务处理里面,等待异步任务触发,就放到任务队列里,比如click和timeout,点击了就加入到任务队列,timeout了也加入到队列里面

本地存储 存放在用户的浏览器里面,不会被刷新删除,容量较大,只能存储字符串,
sessionStorage生命周期是从页面的打开到关闭,刷新不会清除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值