对象
1、创建对象的几种方式?
- 字面量
- new Object();
- 构造函数创建
function Person(name,age,sex,like){
this.name=name;
this.say=function(){
console.log("我叫",name)
}
}
//这一行代码表示的是创建一个对象并且给这个对象的属性进行初始化
let per1=new Person('柯南',19,'男');
per1.say();
- Object.create()
把现有对象的属性,挂到新建对象的原型上(__proto__指向原型),新建的对象享有现有对象的属性。
// new Object() 方式创建
var a = {
rep : 'apple' }
var b = new Object(a)
// Object.create() 方式创建
var a = {
rep: 'apple' }
var b = Object.create(a)
console.log(b) // {}
console.log(b.__proto__) // {rep: "apple"}
console.log(b.rep) // {rep: "apple"}
2、new 的过程发生了什么?
- 创建一个空对象;
- 将空对象原型的内存地址__proto__指向函数的原型对象;
- 将构建函数中的this绑定到新建的对象obj上
- 返回新创建的对象
function Obj(num) {
this.num = num
}
function newFun(cont, ...args) {
//cont是构造函数,args是构造函数的所需参数
// 新建一个对象,new出来返回的就是这个
let obj = Object.create(cont.prototype);
// 给这个对象指定原型链,构造函数有什么,obj也会有
let result = cont.apply(obj, args)
//运行构造函数,把构造函数的参数挂到obj上
return result instanceof Object ? result : obj
}
const test1 = newFun(TestObj,1)
3、什么是对象的原型,什么是原型对象?
原型对象 : prototype,原型对象是构造函数上的一个属性,用来创建公共的方法
// 设置公共方法
Student.prototype.eat = function () {
console.log('吃饭')
}
对象原型 __ __proto____对象原型是实例对象(对象)身上的一个属性
let st = new Student('张三');
st.eat();
console.log(zs.__proto__);
4、如何打印属于对象自身的所有属性
for in
5、所有对象都有自己的原型对象吗
“所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”最终都可以上溯到Object.prototype 。
Object.prototype对象有没有它的原型呢?回答Object.prototype的原型是null。null没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null。”
6、自己实现Object.create方法
function myCreate(obj){
function F(){
};//创建一个新的构造函数
F.prototype=obj;//构造函数的prototype指向obj
return new F();
}
7、原型链的终点是什么?如何打印出原型链的终点?
constructor记录了对象引用于哪个构造函数,可以让原型对象prototype重新指向于构造函数。
8、为什么会有继承,你觉得继承解决了什么问题?
基于原型:
function Parent(name1){
this.name1 = name1
}
Parent.prototype.pMethod = function(){
console.log(this.name1)
}
function Child(name2, name1){
Parent.call(this, name1) // 得分点
this.name2 = name2
}
Child.prototype.__proto__ = Parent.prototype
//上面这句代码的古板写法应该是下面三句
//const empty = function(){}
//empty.prototype = Parent.prototype
//Child.prototype = new empty()
//古板写法额外加分
Child.prototype.cMethod = function(){
console.log(this.name2)
}
基于class
class Parent{
constructor(name1){
this.name1 = name1
}
pMethod(){
console.log(this.name1)
}
}
class Child extends Parent{
constructor(name2, name1){
super(name1) // 得分点
this.name2 = name2
}
cMethod(){
console.log(this.name2)
}
}
9、箭头函数与普通函数的区别
1.没有this、super、arguments和new.target绑定, 箭头函数中的this、super、arguments及new.target这些值由外围最近一层非箭头函数决定
2.不能通过new关键字调用箭头函数没有construct方法,所以不能被用作构造函数,如果通过new关键字调用箭头函数,程序会抛出错误
3.没有原型 由于不可以通过new关键字调用箭头函数,因而没有构建原型的需求,所以箭头函数不存在prototype这个属性。
4.不可以改变this的绑定 函数内部的this值不可被改变,在函数的生命周期内始终保持一致
5.不支持argument对象, 箭头函数没有argument绑定,所以你必须通过命名参数和不定参数这两种形式访问函数的参数
6.不支持重复的命名参数 无论在严格还是非严格模式下,箭头函数都不支持重复的命名参数;在传统函数的规定中,只有在严格模式下才不能有重复的命名参数
10、实现一下new
function _new(fn, ...arg) {
var obj = Object.create(fn.prototype);
const result = fn.apply(obj, arg);
return result instanceof Object ? result : obj;
}
11、call、apply、bind作用与区别?
apply,call和bind都是用来改变this的指向,apply和call会让当前函数立即执行,而bind会返回一个函数,后续需要的时候再调用执行。
call方法: 调用一个对象的一个方法,以另一个对象替代当前的对象。其实就是更改对象的内部指针,即改变对象this的指向
语法:Function.call(thisArg, arg1, arg2, …)
apply方法: apply与call作用一样,只不过第二个参数不同
语法:Function.apply(thisArg, [argsArray])
bind方法: 将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被指定为bind()中的第一个参数的值,
语法:Function.bind(thisArg, arg1, arg2, …)
var a = {
user:"用户",
fn:function(){
console.log(this.user); // 用户
}
}
var b = a.fn;
b.call(a); // 若不用call,则b()执行后this指的是Window对象
b.apply(a, [1, 2]);
var a = {
user: "用户",
fn:function(n1, n2){
console.log(this.user); // 用户
console.log(n1 + n2); // 3
}
}
var b = a.fn;
var c = b.bind(a, 1);
c(2);
call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调用就什么时候调用,并且可以将参数在执行的时候添加,这就是它们的区别。
12、节流防抖
防抖原理:一定在事件触发 n 秒后才执行,如果在一个事件触发的 n 秒内又触发了这个事件,以新的事件的时间为准,n 秒后才执行,等触发事件 n 秒内不再触发事件才执行。
节流:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
//防抖
function debounce(callback,time){
// 定时器变量
let timeId=null
// 返回一个函数
return function(e){
// 判断
if(timeId !=null){
// 清空定时器
clearTimeout(timeId)
}
// 启动定时器
timeId= setTimeout(() => {
// 执行回调
callback.call(this,e)
// 重置定时器变量
timeId = null
}, time);
}
}
//节流 第一次一定会触发
function throttle(callback,wait){
// 定义一个开始时间
let start = 0
// 返回结果是函数
return function(e){
// 获取当前的时间戳
let now = Date.now()
// 判断
if(now - start >wait){
// 如果满足条件,执行回调函数
callback.call(this,e)
// 修改开始时间
start =now
}
}
}
13、数组的方法
- push
- pop删除最后一个元素,改变原数组
- unshift 向第一位添加元素并返回数组长度
- shift删除第一位元素,返回该元素
- join 以指定参数作为分隔符,返回一个字符串
- concat 添加到尾部,不添加元素的时候可以作为浅拷贝
- reserse 颠倒数组,返回改变后的数组,改变原数组
- slice (start,end)提取一部分
- splice(start, count, addElement1, addElement2, …)
10.sort排序,按照字典顺序排序
注意,自定义的排序函数应该返回数值
[10111, 1101, 111].sort(function (a, b) {
return a - b;
})
- map个参数:当前成员、当前位置和数组本身。运行结果组成一个新数组返回,原数组没有变化。
map()方法还可以接受第二个参数,用来绑定回调函数内部的this变量
var arr = ['a', 'b', 'c'];
[1, 2].map(function (e) {
return this[e];
}, arr)
// ['b', 'c']
- forEach不返回值,只用来操作数据
- filter()方法用于过滤数组成员,满足条件的成员组成一个新数组返回。
接受三个参数:当前成员,当前位置和整个数组。
[1, 2, 3, 4, 5].filter(function (elem) {
return (elem > 3);
})
- some、every返回一个布尔值,表示判断数组成员是否符合某种条件。
some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false。
every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false。 - reduce、reduceRight依次处理数组的每个成员,最终累计为一个值。
reduce()是从左到右处理(从第一个成员到最后一个成员),reduceRight()则是从右到左(从最后一个成员到第一个成员),其他完全一样。
[1, 2, 3, 4, 5].reduce(function (a, b) {
console.log(a, b);
return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最后结果:15
- indexOf(),lastIndexOf()
indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1
14、void
让表达式返回undefined
15、正则
\d 匹配0-9之间的任一数字,相当于[0-9]。
\D 匹配所有0-9以外的字符,相当于[^0-9]。
\w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]。
\W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]。
\s 匹配空格(包括换行符、制表符、空格符等),相等于[ \t\r\n\v\f]。
\S 匹配非空格的字符,相当于[^ \t\r\n\v\f]。
\b 匹配词的边界。
\B 匹配非词边界,即在词的内部。
? 问号表示某个模式出现0次或1次,等同于{0, 1}。
- 星号表示某个模式出现0次或多次,等同于{0,}。
- 加号表示某个模式出现1次或多次,等同于{1,}。
- +?:表示某个模式出现1次或多次,匹配时采用非贪婪模式。
*?:表示某个模式出现0次或多次,匹配时采用非贪婪模式。
??:表格某个模式出现0次或1次,匹配时采用非贪婪模式。
i修饰符以后表示忽略大小写
16、with
方便用来引用某个对象中已有的属性
但是不能用来给对象添加属性 要给对象创建
新的属性 必须明确的引用该对象
普通写法:
var car={
size: suv,
color:yellow,
money:1500
};
其他类调用
function={
car car =new car();
car.size=suv;
car.color=yellow;
car.money=1500;
}
使用with的写法:省去了car.
with(car){
size=suv;
color=yellow;
money=1500;
}
缺点1
- with会自动在全局作用域创建一个全局变量,在严格模式下,会抛出ReferenceError 异常。
- with在相同条件下比不使用它慢了很多,具体的原因是因为js在运行之前要进行预编译,
17、eval 计算JavaScript 字符串,并把它作为脚本代码来执行。
eval(“x=10;y=20;document.write(x*y)”);
输出20
18、同步任务和异步任务
同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。
异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。
19、微任务/宏任务
js宏任务有:<script>
整体代码、setTimeout、setInterval、setImmediate、Ajax、DOM事件
js微任务有:process.nextTick、MutationObserver、Promise.then catch finally
实际上异步任务之间并不相同,因此他们之间也有优先级之分,所以任务队列被分成两种类型: 宏任务和微任务。
js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。
事件循环机制(event loop)
- js引擎会从上到下逐行进行解析; 将其中的同步任务按照执行顺序排列到执行栈中,所有的异步任务会放到"任务队列"中;
- 在所有的同步任务执行结束后,在确保没有同步任务的时候,然后检查"任务队列"中是否有任务,如果有,就将第一个事件对应的回调,推到执行栈中执行;
- 异步任务分宏任务和微任务两种类型,微任务比宏任务的执行时间要早,所以会优先把所有的微任务放到执行栈中执行。在执行任何一个宏任务以前(不是队列,是一个宏任务),都会查看微任务队列是否有任务需要清空,也就是宏任务执行以前,必须保证微任务是空的。