JS
基础类型
null是一个为空的object
使用typeof检查null,返回object
undefined为未赋值的变量
使用typeof检查undefined,返回undefined
类型转换
parseInt 转成整数
parseInt(3.14) //3
parseInt('120px') //120
parseInt('rem120') //NaN
parseFloat 转成浮点数
Number强制转换
this
this指向一个对象,称为函数执行的上下文对象
以函数形式调用,this→window
以对象形式调用,this→obj
以构造函数形式调用时,this→所new出来的obj
普通setTimeout声明方式,this指向window,箭头函数指向外层作用域(父执行上下文)对象
//箭头函数this指向
var x =11;
var obj = {
x:22,
say: ()=>{console.log(this.x);},
get: function(){
var test = () =>{console.log(this.x);}
return test();
},
set: ()=>{
var tst = () =>{console.log(this.x);}
return tst();
}
}
obj.say();//11 箭头函数没有this,say指向obj,obj的父执行为window
obj.get();//22 箭头函数没有this,test指向get(正常申明),get的父执行为obj.set();//11 箭头函数没有this,tst指向set同样没有this,set指向obj,obj的父执行为window
arguments
封装实参的对象arguments是一个类数组对象
如何将类数组对象转化为数组
//ES5
Array.prototype.slice.call(arguments)
//ES6
[...arguments]
在调用函数时,所传递的实参都会在arguments中保存
arguments.callee
指向当前函数对象
箭头函数没有arguments对象
数组运算
冒泡排序
var arr = [1,2,3,4,2,1];
for (let i = 0;i <= arr.length -1; i++){ //外循环管趟数
for(let j=0; j<=arr.length - i -1; j++){ //内循环管交换次数
if(arr[j] < arr[j+1]){
let temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp ;
}
}
}
作用域链
链式查找 = 就近原则
function f1(){
let num = 123;
function f2(){
console.log(num);
}
}
let num = 456;
f1(); //输出的num为123
闭包原理:
function a(){
x =123;
function b(){
console.log(x);//123
}
return b;
}
var res = a();//a被调用,执行后链被销毁,返回了b的定义声明
res();//相当于执行了b的定义,链重连 可以访问b、a、go
//b中可以访问到a中的x(每一层ao对象由自己向外scope chain从上到下排列)
//所以a中没法访问b中的内容,a中只能链接自己的ao对象和go对象
//当函数被调用时,会进行执行和销毁,但b被定义时,scopechain和ao对象间的联系又会重连,可以访问到a内的内容
造成内存泄漏:
- 闭包
- 意外的全局变量
- 被遗忘的定时器
- 脱离dom的引用
预解析
js引擎运行js分两步:
①预解析
变量提升:把变量声明提升到当前作用域最前
函数提升:把函数声明提升到当前作用域最前
预解析:1.创建ao(函数内部)/go(全局)对象
2.寻找形参和变量声明 值为undefined
3.实参形参值统一
4.函数体里找函数声明 值赋予函数体
②顺序执行代码
f1();
function f1(){
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
console.log(c);
console.log(b);
console.log(a);
//以上代码的真实执行状态为:
function fa(){
var a = 9; //集体赋值的正确写法为 var a = 9 ,b=9;
b = 9; //没有声明直接赋值,算作全局变量
c = 9;
console.log(a); //9
console.log(b); //9
console.log(c); //9
}
f1();
console.log(c); //9
console.log(b); //9
console.log(a); //报错 变量未定义(局部变量)
构造函数
构造函数与普通函数没有区别,习惯上构造函数首字母大写
调用时,构造函数用new关键字
new时:
①开辟新的空间新建一个obj
②将this指向新建的obj
③执行func里的代码
④将新建的obj返回
function Person (name){
this.name = name; //将全局变量赋值给this(obj)变量
}
var per = new Person("konan");//per被称为Person类的实例
为了防止每次新建实例就新建一个重复函数,并不污染全局作用域,将函数放入共同的prototype对象中
新建的实例中,如果没有对应的key值会去prototype(proto)中寻找,如果有就不会去找
function Person (name){
this.name = name;
}
Person.prototype.func = function(){};
Person.a = "123";
var per = new Person("konan");//per.a = "123"
per.func();
var per2 = new Person("");
var per2.a = "456";//per2.a = "456"
对象中是否存在某个元素
//in方法当自身没有但是原型有也会返回true
console.log("123" in per);//true
//hasOwnProperty只返回自身是否有
cosole.log(per.hasOwnProperty("123"));//false
垃圾回收
当一个对象没有任何的变量或属性对他进行引用,浏览器自动进行回收
DOM
document object model 文档对象模型,通过DOM接口改变网页的内容解构和样式
D:整个HTML文档
O:将整个网页中每一部分转换成一个对象
M:表示对象间的关系
节点Node:网页中的每一部分都是一个节点
document
就是文档节点,指向整个网页
var btn = document.getElementById();
btn.innerHTML = "";//修改文字
btn.onclick = function() {};//增加点击事件
innerHTML 和 createElement的创建效率差
//innerHTML字符拼接方式
var inner = document.querySelector('.inner');
for(var i =0; i<=100; i++){
inner.innerHTML += '<a href="#"></a>'; //innerHTML字符拼接方式最慢 需要3000ms左右
}
//createElement方式
for(var i =0; i<=100; i++){
var a = document.createElement('a');
inner.appendChile(a);//createElement方式需要大概20ms左右
}
//innerHTML 数组方式
var arr = [];
for(var i =0; i<=100; i++){
arr.push('<a href="#"></a>'):
}
inner.innerHTML = arr.join('');//join转化成字符串 最快 只需10ms以内
DOM事件流
给div添加一个点击事件
两种传播顺序
捕获阶段:从父找到子(目标div)
冒泡阶段:从子回到父
js代码中只能执行其中一个阶段
默认为冒泡阶段
事件对象
跟事件相关的一系列的信息数据的集合
event是一个形参
event兼容问题:
e = e || window.event //ie678
e.target
返回的是触发事件的对象(元素)
this
返回的是绑定事件的对象(元素)
<ul><li>123<li></ul>
var ul = document.querySelector('ul');
//点击123时
ul.addEventListenr('click', function(e){
console.log(this); //this→ ul
console.log(e.target); //e.target→ li
}
BOM
浏览器对象模型
将浏览器作为一个对象
顶级为window
同步与异步
同步任务放在主线程执行栈
异步任务(回调函数)放在消息队列
执行完同步才会去执行异步(异步进程处理)
事件循环机制:执行完同步后也会循环检查是否队列里有异步任务
EventLoop
执行代码时将内容压入调用栈,回调函数压入消息队列,promise后续处理内容(then)压入微任务队列,调用栈被清空会才回去按照微任务→消息队列顺序执行
回顾
//输出顺序4 1 3 5 2
var p = new Promise(resolve =>{ //调用栈中加入的微任务会立即执行
console.log(4);
resolve(5);
})
function fun1(){
console.log(1)
}
function fun2(){
setTimeout (()=>{
console.log(2);
},0);//异步操作进入消息队列等调用栈及微任务清空后才会执行
fun1();//执行后从调用栈弹出
console.log(3);
p.then(resolve=>{
console.log(resolve);//进入微任务队列,等待调用栈清空后执行
})
}
fun2();
本地存储
sessionStorage
localStorage
sessionStorage.setItem(key, value)
sessionStorage.getItem(key)
sessionStorage.removeItem(key)
sessionStorage.clear()
jQuery
对原生js封装的库
$
是jQuery的别称,是jquery的顶级对象
//等待DOM结构渲染完毕即可执行内部代码,等同于原生js的DOMContentLoaded
$(function () {
})
jQuery对象与DOM对象不同,jquery用$把DOM元素进行了封装,存在形式为一个伪数组
相互不能混用方法和属性
jQuery对象与DOM对象转换
//DOM → jQuery
var myVideo= document.querySelector('video');
$(myVideo)
//jQuery → DOM
$('video')[0].play();
$('video').get(0).play();
正则表达式
var reg = new RegExp("a", "i"/"g"); // i:忽略大小写 g:全局模式
//等同于 reg = /a/i
var str = "abc";
reg.test(str); //true
reg = /a|b/ == /[ab]/; //a或b
/[a-z]/ 任意小写字母
/[A-z]/ 任意字母
/a[bd]c/ abc或adc
/[^ab]/ 除了ab
call & apply & bind
共同点:能够调用函数,能够改变this的指向
//call使用
let a = {
func(x) {
console.log(x)
}
};
let b ={name:""};
a.func.call(b,"x");//将a内的函数this指向b 并传参x
区别
call/apply直接调用函数
bind返回函数,需要再执行调用指令
//apply
a.func.call(b,"x","y");
a.func.apply(b,["x","y"]);//需要数组形式传参
let c = a.func.bind(b,"x","y");
c();//需要调用
知识试题
1.预编译/预解析
2.this指向/箭头函数的this
3.图片懒加载(data-src)
4.数据扁平化
代码试题
1.赋值/浅拷贝/深拷贝
参考
2.防抖/节流
//防抖:一定时间内不再执行 才执行对应功能 否则清楚计时器 重新计时
function debounce(delay){
let timer;
return function (value){
clearTimeout(timer);
timer = setTimeout( ()=>{
console.log(value);
},delay)
}
}
var input = document.getElementById('input');
var func = debounce(2000);
input.addEventListener('keyup', function(e){
func(e.target.value);
})
//节流:一定时间内 再执行对应功能
function throttle(delay){
let timeOut;
return function(){
if(!timeOut){
timeOut = setTimeout(()=>{
fun();
timeOut = null;
},delay)
}
}
}
todo 图片懒加载与节流防抖的结合应用
闭包实现单例模式
var createLogin = function (a,b,c) {
console.log(a,b,c);
var div = document.createElement("div");
div.innerHTML = '我是登录点击显示内容';
div.style.display = 'none';
document.body.appendChild(div);
return div
}
var getSingle = function (fn) {
var result;
return function () {
return result || (result = fn.call(this,arguments));
}
}
var create = getSingle(createLogin);
document.getElementById("loginButton").onclick = function () {
var loginLay = create(1,2,3);
loginLay.style.display = 'block';
}
实现手写Array.prototype.map
方法
function mapfunc (arr, mapCallback){
if(!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function'){
return []
} else {
let result = [];
let len = arr.length;
for(let i=0; i < len; i++){
result.push(mapCallback(arr[i],i,arr))
}
return result;
}
}
var arr = [1,2,3];
var test = mapfunc(arr,(item,index)=>{
return item * 2;
})
console.log(test);
//prototype的使用方式
var arr = [1,2,3];
var test = arr.map((item,index)=>{
return item * 2;
})
console.log(test);
3.数据扁平化
4.优化if嵌套
提前退出& 提前返回
const printAnimalsDetails = ({type, name, gender} ={}) => {
if(!type) return 'no type'
if(!name) return 'no name'
if(!gender) return 'no gender'
return `${name}` is a ${gender} - ${type}`
}
5.slice(start,end) 不影响原数组 返回新数组,包含start不包含end