标签:function 函数 对象 js 面试 2021 var 事件 undefined
目录
- 1、js的数据类型
- 2、null和undefined的区别
- 3、闭包(必问)
- 4、谈谈异步编程
- 5、let 和 const 以及 var 的区别
- 6、call()、apply()、bind()的区别
- 7、== 与=== 区别
- 8、防抖和节流的区别
- 9、使用new调用函数的时候,会执行怎样的流程
- 10、解释一下js中的原型、原型链
- 11、如何判断数据类型
- 12、列举强制类型转换和隐式类型转换
- 13、请讲述一下事件委托 / 事件代理
- 14、addEventListener和onClick的区别
- 15、讲述一下事件循环机制
- 16、如何阻止事件冒泡
- 17、阻止默认事件
- 18、一个事件触发后,子元素和父元素之间的传播有哪几个阶段/事件流
- 19、简述js中this的指向
- 20、简单解释ajax的工作原理
- 21、请指出document.onload和document.ready两个事件的区别
- 22、解释 JavaScript 中“undefined”和“not defined”之间的区别
- 23、什么是 JavaScript 中的提升操作/变量提升
- 24、实现继承的方法
1、js的数据类型
5种简单数据类型:String,Number,Boolean,Null,undefined。
1种复杂的数据类型:Object。
2、null和undefined的区别
1、null表示没有对象,即该处不应该有值
2、undefined表示缺少值,即此处应该有值,但没有定义
有时在这里面试官会出一些考题
typeof null // "object"
typeof undefined // undefined
null == undefined // true,这里为true是js规定的
null === undefined // false
function fn(data: number = 6) {
return data;
}
console.log(fn(null)); // null
console.log(fn(undefined)); // 6
3、闭包(必问)
这是必问题,一般会配合编程题来问你输出结果,在这里只是简单的阐述一下
含义:能读取其他函数内部变量的函数
可以访问三个作用域中的变量:
a、自己作用域中声明的变量
b、父函数中声明的变量
c、全局作用域中声明的变量
优点:
a、避免全局变量的污染
b、希望一个变量长期存储在内存中(缓存变量)
缺点:
a、内存泄露(消耗)
b、常驻内存,增加内存使用量
应用方式:
a、函数作为返回值(回调)
b、函数作为方法的参数(setTimeout:原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果)
4、谈谈异步编程
含义:Javascript语言的执行环境是"单线程",一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。所以就引入了一步编程的概念。
类型:
- 回调函数
优点:简单、容易理解和实现
缺点:容易写出回调地狱(各个部分之间高度耦合,使得程序结构混乱、流程难以追踪)
ajax(url, () => {});
- 事件监听
特点:异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生
优点:可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化
缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程
f1.on('done', f2); // 事件f1完成done事件后,再执行f2
- 发布订阅
含义:我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)
// f2 向信号中心 jQuery 订阅 done 信号
jQuery.subscribe('done', f2);
// f1 执行完成后,向信号中心 jQuery 发布 done 信号,从而引发 f2 的执行
function f1(){
setTimeout(function() {
jQuery.publish('done')
}, 1000);}
}
// f2 完成执行后,可以取消订阅
jQuery.unsubscribe('done', f2);
- promise
- 生成器 Generators/ yield
特点:可以控制函数的执行
含义:
- 语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
- Generator 函数除了状态机,还是一个遍历器对象生成函数。
- 可暂停函数, yield 可暂停,next 方法可启动,每次返回的是 yield 后的表达式结果。
- yield 表达式本身没有返回值,或者说总是返回 undefined。next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值
// 解决回调地狱问题
function *fetch() {
yield ajax(url, () => {})
yield ajax(url1, () => {})
yield ajax(url2, () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
- async/await
特点:async/await 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里
async/await 函数对 Generator 函数的改进,体现在以下三点:
1、内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
2、更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)
3、更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
5、let 和 const 以及 var 的区别
- var 全局变量,存在变量提升,在声明前取值为undefined 会挂载在window上
- let 声明的变量只在它所在的代码块有效
- const 声明后不能再修改其指向的目标,假如const 指向的是一个对象/数组,那么虽然不能更改指向目标,但是可以更改对象和数组内部的值
6、call()、apply()、bind()的区别
作用:显式绑定修改函数this的指向
区别:
- call 与 apply 方法都是挂载在 Function 原型下的方法,所有的函数都能使用
- call和apply的第一个参数会绑定到函数体的this上,如果不传参数,例如 fun.call(),非严格模式,this默认还是绑定到全局对象
- call函数接收的是一个参数列表,apply函数接收的是一个参数数组
- call和apply会立即绑定
var person ={
"name":"koala"
};
function changeJob(company,work){
this.company = company;
this.work = work;
};
changeJob.call(person,'百度','程序员');
console.log(person.work);// '程序员'
changeJob.apply(person,['百度', '测试']);
console.log(person.work); //测试
- call和apply这两个方法在调用的时候,如果我们传入数字或者字符串,这两个方法会把传入的参数转成对象类型
var number = 1 , string = '程序员成长指北';
function getThisType(){
var number = 3;
console.log('this指向内容',this);
console.log(typeof(this))
}
getThisType.call(number)
getThisType.apply(string)
// 输出结果
// this指向内容 [Number: 1]
// object
// this指向内容 [String: '程序员成长指北']
// objec
- bind 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数
- bind 方法不会立即执行
var publicAccounts = {
name:'测试11111',
author:'hoster',
subscribe:function(subscriber){
console.log(subscriber+this.name)
}
}
publicAccounts.subscribe('aa')
//"aa 测试11111"
var subscribe1 = publicAccounts.subscribe.bind({name:'测试bbb',author:'持有者'},'bb')
subscribe1()//bb测试bbb
7、== 与=== 区别
== 会先做类型隐式转换,再判断值的大小
=== 类型和值必须都相等
8、防抖和节流的区别
共同点:都用到了setTimeout定时器,都是在指定时间间隔后执行函数,都是为了解决数据时时变化而时时请求导致性能差的问题
- 防抖:当频繁的触发后,并不会立即调用方法,只有在经过指定的间隔内没有输入的情况下> 才会调用函数方法
解决原理 :对处理函数进行延时操作,若设定的延时到来之前再次触发事件,则清除上一次的延时操作定时器,重新定时。- 节流:在高频触发下,指定时间间隔内只会执行一次任务(input一直输入的情况下每隔一段时间会触发一次函数)
8.1 原生编程
有些公司的笔试题会出这种原生编程防抖节流代码
// 防抖
var input = document.getElementById('input')
input.addEventListener('input', debounce(handle, 1000))
function debounce(fn,delay){
var timer
return function(...args){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,args)
}, delay)
}
}
function handle(e){
console.log(e.target.value)
}
// 防抖
var input = document.getElementById('input')
input.addEventListener('input', debounce(handle, 1000))
function debounce(fn,delay){
var timer
return function(...args){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,args)
}, delay)
}
}
function handle(e){
console.log(e.target.value)
}
9、使用new调用函数的时候,会执行怎样的流程
1、创建一个空对象
2、将空对象的 proto 指向原对象的 prototype
3、执行构造函数中的代码
4、返回这个新对象
9.1 原生编程
function create () {
// 1. 创建新的对象。
let obj = new Object()
// 2.利用 shift 拿到 arguments 的第一个参数,也就是构造函数。
const Func = [].shift.call(arguments)
// 3.将新对象的 __proto__ 与构造函数的 prototype 挂钩。
obj.__proto__ = Func.prototype
// 4.将新对象作为上下文执行构造函数。
const Res = Func.apply(obj, arguments)
// 5.判断返回值是否为对象,若是对象则返回该对象,否则返回新对象。
return Res instanceof Object ? Res : obj
}
function Person (name) {
this.name = name || '我是名字'
}
Person.prototype.sayName = function () {
console.log('sayName >>>', this.name)
}
let p1 = create(Person, '老李')
p1.sayName()
10、解释一下js中的原型、原型链
原型: 每创建一个函数,函数上都有一个属性为 prototype,它的值是一个对象。 这个对象的作用在于当使用函数创建实例的时候,那么这些实例都会共享原型上的属性和方法
原型链: 在 JavaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接(proto)。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向)。这种一级一级的链结构就称为原型链(prototype chain)。 当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止;到查找到达原型链的顶部(Object.prototype),仍然没有找到指定的属性,就会返回 undefined
11、如何判断数据类型
typeof(区分基本数据类型)
基本数据类型,例如Number,String,Boolean,Function,undefined可以使用,对于引用数据类型,例如Array,Object,null,Date,RegExp,Error这几个类型都是Object
typeof [1,2,3], //"object"
typeof new Date(), //"object"
typeof /^[a-zA-Z]{5,20}$/, //"object"
typeof new Error(), //"object"
typeof Object // function
typeof Array // function
instanceof(只适用于对象类型,不用于简单类型,返回boolean)
作用:判断一个对象是否属于某个构造函数的实例
原理:左边是实例对象,右边是构造函数,instanceof会检查构造函数的原型对象propotype是否在左边对象的原型链上,有则返回true
// 需要注意null和undefined都返回了false
// 这是因为它们的类型就是自己本身,并不是Object创建出来它们,所以返回了false
undefined instanceof Object, //false
null instanceof Object, //false
// 基本数据类型判断都为false
123 instanceof Number, //false
// 修改方案
var num = new Number(123);
num instanceof Number, // true
constructor(判断所有的数据类型)
原理:construtor是prototype对象上的属性,指向构造函数根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的
// 除了undefined和null之外,其他类型都可以通过constructor属性来判断类型
function Person(){}
var tom = new Person();
tom.constructor == Person, // true
var error= new Error();
error.constructor == Error // true
jQuery.type()(判断所有的数据类型)
jQuery.type(str) //string
jQuery.type(num) //number
jQuery.type(arr) //array
jQuery.type(obj) //object
jQuery.type(fn) //funtion
toString()(判断所有的数据类型)
使用规则:为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数
toString.call(123); //"[object Number]"
toString.call(new Date()); //"[object Date]"
toString.call(/^[a-zA-Z]{5,20}$/); //"[object RegExp]"
toString.call(new Error()); //"[object Error]"
12、列举强制类型转换和隐式类型转换
强制类型转换:toString()、String()、Number()、parseInt()、parseFloat()、Boolean()
toString(): 该方法不会影响到原来的变量,它会将转换的结果返回,null和undefined两个值没有该方法
var a = 123;
a.toString();
console.log(a); // 123
var b = a.toString();
console.log(b); // "123"
Number(): 如果是纯数字的字符串,则直接转换为数字,如果字符串中有非数字的内容,则转换为NaN,如果字符串是一个空串或者是一个全是空格的字符串,则转换为0
Number(true) // "1"
Number(Null) // 0
Number(undefined) // "NaN"
parseInt()、parseFloat(): 主要是针对字符串的,对于含有数字和字符的字符串,如果第一个为字符串则为NaN,否则提取字符之前的数字,其余的只要没有数字的全为NaN
Boolean():除了0、NaN、空串、null、undefined其余都是true。对象也会转换为true
隐式类型转换:==、+
/* 字符串 + 数字 => 数字转化成字符串 */
10 + '20' // 1020
/* 数字 - 字符串 => 字符串转化成数字(如果字符串不是纯数字则为NaN)*/
/* 乘、除、<、>一样的操作 */
10 - '20' // -10
10 - '100a' // NaN
/* 字符串 == 数字 => 字符串转数字 */
'0' == 0 // true
13、请讲述一下事件委托 / 事件代理
是利用事件冒泡的特性,将本应该绑定在多个元素上的事件绑定在他们的祖先元素上
优点:
1)在动态添加子元素的时候,可以非常方便的提高程序性能
2)可以大量节省内存占用,减少事件注册
缺点:可能会出现事件误判,就是本不该被触发的事件被绑定上了事件
13.1 原生编程
window.onload = function(){
var oUl = document.getElementById("ul1");
oUl.onclick = function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
// 所有子元素的响应时间一致的情况
alert(123);
// 不同子元素响应不同的事件
switch(target.id){
case 'add' :
alert('添加');
break;
case 'remove' :
alert('删除');
break;
}
}
}
}
14、addEventListener和onClick的区别
- onclick为同一个元素绑定的事件,后面的事件会覆盖之前的事件,执行一次后面的事件
- addEventListener可以多次绑定同一个事件并且不会覆盖上一个事件
addEventListener的第三个参数表示回调函数在事件流的哪个阶段执行,冒泡阶段(false),捕获阶段(true)
15、讲述一下事件循环机制
javascript中任务分为同步任务和异步任务
1、所有任务都在主线程上执行,形成一个执行栈
2、主线程之外,还存在一个"任务队列",只要异步任务有了运行结果,就在"任务队列"之中放置一个事件
3、一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行
4、主线程不断重复上述的第三步
16、如何阻止事件冒泡
e.cancelBubble = true; // ie浏览器
e.stopPropagation(); // 非ie
17、阻止默认事件
return false; // 最直接方式
e.preventDefault(); // ie下都可以使用
window.event.returnValue = false; // ie下
18、一个事件触发后,子元素和父元素之间的传播有哪几个阶段/事件流
事件流描述的是从页面中接收事件的顺序
1、捕获阶段:(上层到底层)从window对象传导到目标节点,该阶段不会响应任何事件
2、目标阶段:在目标节点上触发
3、冒泡阶段:(从底层到上层)从目标节点传导回window对象
19、简述js中this的指向
this的指向在函数定义的时候是确定不了的,实际上this的最终指向的是那个调用它的对象
调用形式 | this指向 | 原因 |
---|---|---|
普通的函数/匿名函数 | window | 执行环境为window |
构造函数不用new创建对象 | window | 作为普通函数被window调用 |
构造函数用new创建对象 | 该新对象 | |
对象方法的调用 | 该对象 | |
call/apply/bind | 传入的第一个参数值 | |
严格模式下 | undefined | |
dom节点 | dom对象 |
20、简单解释ajax的工作原理
1、创建ajax对象(XMLHttpRequest -> 用来和服务器交换数据 /ActiveXObject(‘Microsoft.XMLHttp’))
2、使用XMLHttpRequest的open(),请求方式(get/post)、请求地址、是否是同步
3、使用XMLHttpRequest的send()发送请求
4、当ajax对象完成发送,通过onreadystatechange监听数据接收状态,判断http响应状态(state)200-300之间或304(缓存执行)回调函数
21、请指出document.onload和document.ready两个事件的区别
ready:在DOM载入就绪时进行操作
onload:网页中所有的元素包括js,css,图片等完全加载到浏览器后才执行,只执行一次
22、解释 JavaScript 中“undefined”和“not defined”之间的区别
- 当我们试图访问一个被声明但未被定义的变量时,会出现 undefined 错误
var x; // 声明
if(typeof x === 'undefined') // 将返回 true
- 当我们试图引用一个既未声明也未定义的变量时,将会出现 not defined 错误
console.log(y); // 输出: ReferenceError: y is not defined
23、什么是 JavaScript 中的提升操作/变量提升
提升(hoisting) 是 JavaScript 解释器将所有变量和函数声明移动到当前作用域顶部的操作
提升顺序:
- 先执行变量提升,后执行函数提升
- 函数声明的优先级高于变量,如果变量名和函数名相同且未赋值,则函数声明会覆盖变量声明,否则报错
在这种情况下,会有相关的编程输出题考察
var a = 2;
foo(); // 因为`foo()`声明被"提升",所以可调用
function foo() {
a = 3;
console.log( a ); // 3
var a; // 声明被"提升"到 foo() 的顶部
}
console.log( a ); // 2
// 2.
var c = 1; // 其实该处是先进行声明 var c;
function c(c) {
console.log(c);
}
// 然后在该处进行赋值 c = 1;
c(2); // 报错
24、实现继承的方法
- 原型链继承
- 借用构造函数继承
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
标签:function,函数,对象,js,面试,2021,var,事件,undefined 来源: https://blog.csdn.net/C_fashionCat/article/details/115630055
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。