undefined、symbol、string、number、null、boolean、bigInt、object
1.种类
1.typeof
2.instanceof(根据原型链判断)
[] instanceof Array ; //true
手写实现instanceof
function myInstance(L,R){
//获取判断对象隐式原型
var LP = L._proto_
//判断类型的显示原型
var RP = R.prototype
while(true){
if(LP == null) return false
if(LP == RP) return true
LP = LP._proto_
}
console.log(myIntance({},object))
}
3.对象的构造器:constructor
4.对象原型链判断:Object.prototype.toString.call
1 Object.prototype.toString.call("a")
"[object String]"
2 Object.prototype.toString.call(undefined)
"[object Undefined]"
3 Object.prototype.toString.call(null)
"[object Null]"
4 Object.prototype.toString.call(new Date())
"[object Date]"
new操作符通过构造函数创建的实例,可以通过访问构造函数的属性和方法,也将实例和构造函数通过原型链连接起来
1.创建一个新对象,并且在内存中创建一个新的地址
let obj = new Obect()
2.设置创建对象的原型链继承构造函数的原型
obj._proto_ = 构造函数的.prototype
3.绑定构造函数的this的指向,将构造函数的this指向创建的对象,并且执行函数体
let result =Fn.call(创建的对象);
4.判断构造函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
return result instanceof Object? result: obj;
1.基本概念
2.主要区别
dom.onclick:
dom.addEventListener
JS中常用的内置对象:Array对象、Date对象、正则表达式对象、string对象、Global对象
方法:
这些内置对象中的一些方法
各方法详情
属于短路运算符(例如第一个操作数决定了结果,就不会对第二个操作数求值)
补充
作用域
作用域链
上下级之间的不同作用域构成作用域链
JS引擎在执行代码的时候1.预解析2.执行代码
预解析:JS引擎将JS所有以Var声明的变量和全局的function都提升到作用域的最前面
变量提升:将所有的变量声明提升到当前作用域的最前面,无赋值操作
作用都是用来重新定义 this 这个对象的!
//调用父级的同时将父级函数里面的this指向子函数里面的this,使子函数可使用父函数的里面的方法和属性
father.myFun.call(son,'成都','上海');
father.myFun.apply(son,['成都','上海']);
father.myFun.bind(son,'成都','上海')();
1.剩余参数(拓展运算符)只包含那些没有对应形参的实参,而 arguments (对应传递给函数参数的类数组对象)包含了传给函数的所有实参。
2.arguments对象不是一个真正的数组,而剩余参数是真正的 Array实例,也就是说你能够在它上面直接使用所有的数组方法,比如 sort,map,forEach或pop。
3.arguments对象还有一些附加的属性 如callee属性)。
详情:http://t.csdn.cn/xe0dd
一、遍历数组的方法
arr.forEach(function(item,index,arr){
console.log("元素:"+item+" 索引:"+index+" 整个数组:"+arr);
arr.map(function(val,index){
console.log("元素:"+val+" 索引:"+index);
return val*val;
for(let item of arr){
console.log("元素:"+item);
二、遍历对象的方法
let obj1 = {
a:"A",
b:"B",
c:"C"
}
for(let ch in obj1){
console.log(ch); a b c
}
这个方法遍历字符串的话
let str = “abcdefj”
let obj1 = {
a:"A",
b:"B",
c:"C"
}
console.log(Object.keys(obj1)); //(3) ['a', 'b', 'c']
这个方法遍历字符串的话
let str = “abcdefj”
let obj1 = {
a:"A",
b:"B",
c:"C"
}
console.log(Object.values(obj1)); //(3) ['A', 'B', 'C']
这个方法遍历字符串的话
let str = “abcdefj”
let obj1 = {
a:"A",
b:"B",
c:"C"
}
console.log(Object.getOwnPropertyNames(obj1)); //Array(3) ['a', 'b', 'c']
这个方法遍历字符串的话
let str = “abcdefj”
三、遍历字符串的方法
NaN 属性是代表非数字值的特殊值。该属性用于指示某个值不是数字。可以把 Number 对象设置为该值,来指示其不是数字值。
parseInt("blabla"))
typeof NaN; // "number"
简单总结:undefined代表了不存在的值(对一个值声明后,没有赋值,输出他就是 undefined ,是不存在的),null代表了没有值(当我们赋值为 null, 那么输出他就是 null,也就是为空没有了值。)
1.
undefined == null //true, “==” 进行了隐式转换,undefined的值 是 null 派生来的,所以他们表面上是相等的
undefined === null //false
2.
let a;
typeof a;//undefined
let b= null;
typeof b;//object
这里为什么typeof b 输出为 Object 呢?
答:null 不是一个对象,尽管 typeof age输出的是 Object,逻辑上讲, null 值表示一个空对象指针 ,这是一个历史遗留问题,JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,null表示为全零,所以将它错误的判断为 Object 。
主要区别:在于 == 对比时,若类型不相等,会先转换为相同类型,然后再来比较值。而 === 则不会,只能在相同类型下比较值,不会做类型转换。还有一个是 = ,这个是赋值,不是运算符。
1、 = = =
下面的规则用来判断两个值是否===相等:
console.log(0.1 + 0.2) // 结果是0.30000000000000004,而不是3
JavaScript 是一种弱类型的、动态的语言。
日期时间格式转换
声明实例化构造函数
var date = new Date()
时间戳:
var nowDate = date.getTime() //返回从1970年到现在的毫秒数
let timestamp =(new Date()).valueOf();//返回从1970年到现在的毫秒数
var year = date.getFullYear() //获取年
var month = date.getMonth() + 1 //获取月 (0 ~ 11)
var day = date.getDate() //获取天(1-31)
var hours = date.getHours() //获取小时 (0 ~ 23)
var m = date.getMinutes()//获取分(0 ~ 59)
var s = date.getSeconds()//获取秒(0 ~ 59)
month < 10 ? month = "0" +month : month
day < 10 ? day = "0"+day : day
hours < 10 ? hours = "0" + hours : hours
m < 10 ? m = "0" + m : m
s < 10 ? s = "0" + s : s
var nowDate = year + "年" + month + "月" + day + "日" +hours+"时"+ m + "分"+ s + "秒"
console.log(nowDate)
倒计时函数
function countDown(time) {
//前面添加+ 返回的格式就是毫秒数 否则Sun May 01 2022 21:40:47 格式
let nowTime = +new Date(); //返回的是现在的总毫秒数
let inputTime = +new Date(time);//返回的是用户输入的总毫秒数
// 1000 将毫秒数转换为秒数
let times = (inputTime - nowTime) / 1000;
// 分- 时 - 天
let d = parseInt(times / 60 / 60 / 24);
d = d < 10 ? '0' + d : d;
//分-时 - 余数(一天24小时余下的那个数)
let h = parseInt(times / 60 / 60 % 24);
h = h < 10 ? '0' + h : h;
// 分 - 余数
let m = parseInt(times / 60 % 60 );
m = m < 10 ? '0' + m : m;
//余数
let s = parseInt(times % 60);
s = s < 10 ? '0' + s : s;
return d + '天' + h + '时' + m + "分" + s + '秒';
}
//输入的时间一定比现在的时间大,因为是倒计时
console.log(countDown('2022-6-1 18:00:00'));
ength 是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数。形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数。
一般function有多少个形参,length就是多少
function fn1 () {}
function fn2 (name) {}
function fn3 (name, age) {}
console.log(fn1.length) // 0
console.log(fn2.length) // 1
console.log(fn3.length) // 2
如果有默认参数
function fn1 (name) {}
function fn2 (name = '林三心') {}
function fn3 (name, age = 22) {}
function fn4 (name, aaa, age = 22, gender) {}
function fn5(name = '林三心', age, gender, aaa) {}
console.log(fn1.length) // 1
console.log(fn2.length) // 0
console.log(fn3.length) // 1
console.log(fn4.length) // 2
console.log(fn5.length) // 0
剩余参数
剩余参数是不算进length的计算之中的
function fn1(name, ...args) {}
console.log(fn1.length) // 1
事件三要素:事件源(处理对象),绑定事件,添加事件处理程序
事件流:
// 兼容性问题8后都不支持
//系统自动创建,不需要传递参数
var div = document.querySelector('div')
//系统自动创建,不需要传递参数
div.addEventListener('click',function(event){
// 都是div 两者之间的区别
console.log(event.target); //div 触发事件的元素(那个元素点击)
console.log(this); //div 绑定事件的元素(哪个元素绑定)
event.cancelBubble // 阻止事件冒泡 标准写法 event.propagation()
event.preventDefault() //阻止默认行为
})
在 JavaScript 中,通过 new 来实例化对象的函数叫构造函数,也就是初始化一个实例对象,对象的 prototype 属性是继承一个实例对象。构造函数的命名一般会首字母大写~
构造函数的作用:
1.创建一个新对象,并且在内存中创建一个新的地址
let obj = new Obect()
2.设置创建对象的原型链继承构造函数的原型
obj._proto_ = 构造函数的.prototype
3.绑定构造函数的this的指向,将构造函数的this指向创建的对象,并且执行函数体
let result =Fn.call(创建的对象);
4.判断构造函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
return result instanceof Object? result: obj;
new 操作符通过构造函数创建的实例,可以访问构造函数的属性和方法,同时实例与构造函数通过原型链连接起来了。
构造函数的返回值
构造函数中,不要自定义显式返回任何值,构造函数会自动返回
**作用:**构造函数和原型原型链为了构造函数和原型都是为了创建对象(解决JS中继承以及内存消耗)
构造函数的最主要问题在于,其定义的方法会在每一个实例上都创建一遍。
function Person(name) {
this.name = name;
this.age = 18;
this.say = function () {
console.log("你好,我今年" + this.age);
}
}
let person1 = new Person("a");
let person2 = new Person("b");
console.log(person1.say == person2.say) //false
**解决办法:**将属性或者方法定义在构造函数的原型上,从而创建的实例可以通过原型的查找去找到方法,并且不同的实例所拥有的方法都是构造函数原型上的方法(原型模式定义的属性和方法是由所有的实例所共享的)
function Person(name) {
Person.prototype.name = name;
Person.prototype.age = 18;
Person.prototype.say = function () {
console.log("你好,我今年" + this.age);
}
}
let person1 = new Person("a");
let person2 = new Person("b");
console.log(person1.say == person2.say) //true
**原因:**构造函数只要创建后,就会有一个 prototype 属性指向它的原型对象,而这个原型对象也有一个 constructor 属性指向构造函数
关于原型和原型链的作用和特性
中文在数据库中存放是占两个字符的,但是在浏览器中,由于 javascript 是 unicode 编码的,所有的字符对于它来说一个就是一个,获取的是中文的长度而不是字符的长度
conlog.log("你好呀大笨蛋".length) //6
这就会导致前后端的对于中文的验证长度不一样了,如何解决?
function getRealLength( str ) {
return str.replace(/[^\x00-\xff]/g, '__').length; //这个把所有双字节的都给匹配进去了
}
session 是服务端的状态保存机制
cookie 是客户端的状态保存机制
当第一次访问服务器的时候 服务器会开辟一块空间用来存放用户的信息
每一个登录之后的用户信息 都会以key value格式记录在session中
同时服务器会把sessionId(用户信息) 存在cookie中 返回给访问的客户端
客户端就会把sessionID保存在本地的cookie中对应的网站记录下
下次访问的时候会携带这个sessionId
服务器会验证这个cookie的有效性 来判断用户是否登录
数据缓存
返回字符串第一个字符的 Unicode 编码(H 的 Unicode 值): charCodeAt() 方法可返回指定位置的字符的
Unicode 编码,返回值是 0 - 65535 之间的整数,表示给定索引处的 UTF-16 代码单元。 字符串中第一个字符的位置为0, 第二个字符位置为 1,以此类推。
两者区别:
1.for-in只是获取数组的索引;而for-of会获取数组的值
2.for-in会遍历对象的整个原型链,性能差;而for-of只遍历当前对象,不会遍历原型链
3.对于数组的遍历,for-in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性);for-of只返回数组的下标对应的属性值
4.for-of适用遍历数组/字符串/map/set等有迭代器对象的集合,但是不能遍历普通对象(obj is not iterable)
5.for-of只遍历当前有迭代器对象的集合,不会遍历原型链:
2. 焦点事件
1. onblur:失去焦点
2. onfocus:元素获得焦点。
3. 加载事件:
1. onload:一张页面或一幅图像完成加载。
4. 鼠标事件:
1. onmousedown 鼠标按钮被按下。
2. onmouseup 鼠标按键被松开。
3. onmousemove 鼠标被移动。
4. onmouseover 鼠标移到某元素之上。
5. onmouseout 鼠标从某元素移开。
5. 键盘事件:
1. onkeydown 某个键盘按键被按下。
2. onkeyup 某个键盘按键被松开。
3. onkeypress 某个键盘按键被按下并松开。
6. 选择和改变
1. onchange 域的内容被改变。
2. onselect 文本被选中。
7. 表单事件:
1. onsubmit 确认按钮被点击。
2. onreset 重置按钮被点击。
JS常见的方法和内置属性:
严格模式指的是js引擎以严格模式去执行,我们只要在代码前或者函数前添加“use strict”就可以开启严格模式了。在严格模式下对js代码的执行添加了一些限制,主要是为了保证代码在安全环境中执行,减少不必要的错误,为了消除js中的一些不严谨、不合理、不安全的地方,它代表了js一种更加合理、更加安全、更加规范的一个发展方向。
严格模式下,变量必须声明再使用。
严格模式下,预编译时this为undefined;
严格模式下,不支持arguments、caller、callee、with;
严格模式下,拒绝重复的属性和参数;
严格模式下,局部的this必须被赋值、赋值什么就是什么。
在全局执行上下文的this和全局执行上下文的函数内this不再指向windon对象
不允许使用未声明的变量。对象也是一个变量。
不允许对变量或函数使用delete操作符
不允许变量重名
不允许使用八进制
禁止this关键字指向全局对象
不可在if内部声明函数
不允许不使用 var 关键字去创建全局变量,抛出 ReferenceError
不允许对变量使用 delete 操作符,抛 ReferenceError
不可对对象的只读属性赋值,不可对对象的不可配置属性使用 delete 操作符,不可为不可拓展的对象添加属性,均抛 TypeError
对象属性名必须唯一
函数中不可有重名参数
在函数内部对修改参数不会反映到 arguments 中
淘汰 arguments.callee 和 arguments.caller
不可在 if 内部声明函数
抛弃 with 语句
~~true == 1
~~false == 0
~~“” == 0
~~[] == 0
~~undefined ==0
~~!undefined == 1
~~null == 0
~~!null == 1
具体转化哪些变量有用呢:
数字类型的字符串可以转化为纯数字
var a=‘123’;
console.log(~~a); //输出123
字符串中带了其他字母,符号,或者其他除数字外的东西,一律输出 Number类型的0
var a=‘asd’;
console.log(~~a); //输出0
任何boolen类型的,如果为TRUE则输出1,FALSE输出0;
var a=1==1;
console.log(~~a);//输出1
特殊类型,转化为Boolean是true的输出1,转化为boolean是false的输出0;
var a=undefined;
console.log(~~a);//输出0
var b=!undefined;
console.log(~~b);//输出1
详情
位运算符有 7 个,分为两类:
逻辑位运算符:位与(&)、位或(|)、位异或(^)、非位(~)
移位运算符:左移(<<)、右移(>>)、无符号右移(>>>)、无符号左移(>>>)
详情
delete是普通运算符,会返回true或false。
(我回答内容是Content-Length加Last-modified的哈希时,面试官竟然肉眼可见的惊喜…)
var
第一种:Object.freeze
也就是该 对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。该属性常常用在我们从接口拿到的数据,为了避免发生了修改,会拿到后对级冻结
该方法可以冻结对象,被冻结的对象有下面的特性:
freeze 做了什么 ?
seal 做了什么?
● 设置 Object.preventExtension(),禁止添加新属性 ( 绝对存在 )
● 设置 Configurable为 false,禁止配置 ( 绝对存在 )
● 禁止更改访问器属性 ( getter和setter )
第三种: Object. preventExtensions
让一个对象变的不可扩展,也就是永远不能再添加新的属性。
一、函数
JavaScript中这样一段代码。只被定义一次,但是可以在需要的场景执行或者调用任意次
常见的定义方式有下面这几种
● 函数声明式 : function functionName (){}
● 函数表达式:let name = function(){}
● 箭头函数:()=> {}
● 构造函数式:let name = new Function (arg1 , arg2 ,arg3 ,…, argN , body )
ps:第一种和第二种函数的定义的方式其实是第四种构造函数的语法糖,当我们定义函数时候都会通过 new Function 来创建一个函数,只是前两种为我们进行了封装,我们看不见了而已,js 中任意函数都是Function 的实例,比如下面:
symbol 是 ES6中新添加的基本数据类型。它的作用是定义一个唯一的对象属性名,也就是唯一的标签,且不可变。
作用:
//可以传入参数,参数书对 symbol 的描述,可以调用,但是不能访问 symbol 本身
var sym1 = Symbol();
var sym2 = Symbol('foo');
var sym3 = Symbol('foo');
console.log(typeof sym1); // symbol
console.log(Symbol("foo") === Symbol("foo")); // false
console.log(sym2 == sym3 );//false
//Symbol 包装器对象作为属性的键
var name = Symbol("名字");
var obj = {};
obj[name] = "大黄";
// 作为对象属性 key 时,在获取其值的时候,只能使用 [] 形式获取,不能使用 . 操作符
console.log(obj);//{Symbol(name): "大黄"}
console.log(obj[name]);//大黄
console.log(obj.name);//undefined
//不能被 for in
var name = Symbol("名字");
var obj = {};
obj[name] = "大黄";
for(var key in obj){
console.log(obj[key]);//无输出
}
// 用作唯一常量,标识符等
var animal= {
rabbit: Symbol(),
dog: Symbol(),
snake: Symbol()
}
解决方案: tiny-async-pool、es6-promise-pool、p-limit
展开运算:允许一个表达式在某处展开。展开运算符在多个参数(用于函数调用)或多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方可以使用。
在ES6之前将整个数组里面的元素依次作为实参传递给函数形参的时候使用Function.prototype.apply的特性
let arr = [1,2,3]
function test(a,b,c){}
test.apply(null,args) //通过apply特性将数值的形式转换为数组对应传递进去
ES6之后展开运算符
let arr = [1,2,3]
function test(a,b,c){}
test(..arr) //将数组展开进行传递参数
a.合并数组
let arr = [1,2,3]
let arr1 = [...arr,4,5,6] //1,2,3,4,5,6
b.展开运算符可以用于数组的一些方法中(push函数)
let arr = [1,2,3]
let arr1 = [4,5,6]
arr1.push(...arr) //4,5,6,1,2,3
c.类数组对象变成数组
let a=new Set([1,2,3,4,5,2,1]) // a : Set(5) {1, 2, 3, 4, 5}
let b=[...a] // (5) [1, 2, 3, 4, 5]
let [arg1,arg2,...arg3] = [1, 2, 3, 4]
arg1 //1
arg2 //2
arg3 //['3','4']
let {x,y,...z}={x:1,y:2,a:3,b:4};
x; //1
y; //2
z; //{a:3,b:4}
let z={a:3,b:4};
let n={x:1,y:2,...z};
n; //{x:1,y:2,a:3,b:4}
let a={x:1,y:2};
let b={z:3};
let ab={...a,...b};
ab //{x:1,y:2,z:3}
剩余参数:剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
function test(a,b,...args){} //...args == [4,5,6]
test(1,2,3,4,5,6)
let [arg1,arg2,...arg3] = [1, 2, 3, 4]
arg1 //1
arg2 //2
arg3 //['3','4']
二、arguments 对象
1.定义:arguments是一个对应传递给函数参数的类数组对象,arguments对象是所有非箭头函数都有的一个局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处。
2.特性:
const args = Array.from(arguments);
const args = [...arguments];
属性callee相当于调用自身函数,可以用作匿名函数的递归:
var sum = function (n) {
if (1 == n){
return 1;
} else {
return n + arguments.callee(n - 1); //6 5 4 3 2 1 } } alert(sum(6)); 输出结果:21
}
3.作用:
a.无需明确命名参数,就可以重写函数,在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们
function sayHi(message) {
alert(arguments[0]); // 此处将打印message参数的值
}
b.检测参数个数( arguments.length )
function howManyArgs() {
alert(arguments.length);
}
howManyArgs("string", 45);
howManyArgs();
howManyArgs(12); // 上面这段代码将依次显示 "2"、"0" 和 "1"。
c.针对同一个方法被多处调用,但是参数数量不确定的情况下,可以更具arguments索引进行判断。
function func1() {
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
console.log(arguments[2]); // 3
}
func1(1, 2, 3)
d.模拟函数重载
用 arguments 对象判断传递给函数的参数个数,即可模拟函数重载
当只有一个参数时,doAdd() 函数给参数加 5。如果有两个参数,则会把两个参数相加,返回它们的和。所以,doAdd(10) 输出的是 “15”,而 doAdd(40, 20) 输出的是 “60”。
function doAdd() {
if(arguments.length == 1) {
alert(arguments[0] + 5);
} else if(arguments.length == 2) {
alert(arguments[0] + arguments[1]);
}
}
doAdd(10); //输出 "15"
doAdd(40, 20); //输出 "60"
ArrayBuffer对象是 JavaScript 操作二进制数据的一个接口。属于独立的规格(2011 年 2 月发布),ES6 将它们纳入了 ECMAScript 规格。它以数组的语法处理二进制数据,所以称为二进制数组。
二进制数组就是在这种背景下诞生的。它允许开发者以数组下标的形式,直接操作内存,大大增强了 JavaScript 处理二进制数据的能力。
二进制数组由三类对象组成
ArrayBuffer、TypedArray、DataView
简单说,ArrayBuffer对象代表原始的二进制数据,TypedArray视图用来读写简单类型的二进制数据,DataView视图用来读写复杂类型的二进制数据。