前言:
第一章为变量类型和计算。
第二章到第四章分别是原型与原型链,作用域及闭包,异步和单线程,这三个就是我们说的三座大山。
一、变量类型和计算
1:JS中使用typeof能得到哪些类型?
字符串(String)、数值(Number)、布尔值(Boolean)、Undefined、[Null对象,数组(Array),函数(Function) ](Object)共5种
2:何时使用===,何时使用==
两等:当数据类型不一样时,会发生强制转换,然后比较值是否相等
三等:数据类型相同,数据的值也相同,不会发生类型转换,才能为true
除了if(obj.a == null){}用(==),其余情况建议用(===),因为jq源码是这么写的
3:js中有哪些内置函数
Object、Array、Boolean、Number、String、Function、Date、RegExp、Error
4:js变量按照存储方式区分哪些类型,并描述其特点
答:按储存方式区分变量类型:
值类型:字符串(String)、数值(Number)、布尔值(Boolean)、Undefined、Null
这6个占用空间固定,保存在内存空间(栈)中/保存与复制的是值本身/使用typeof检测数据的类型
引用类型:对象(Object)、数组(Array)、函数(Function)
这3个占用空间不固定,保存在堆中/保存与复制的是指向对象的一个指针/使用instanceof检测数据类型/使用new()方法构造出的对象是引用型
js中,值传递不会改变值,引用类型传递会改变指针指向,会改变他的值
5:如何理解json
JSON :JSON和Math一样都是内置对象 JSON也是一种数据格式 有两个API如下面所示:
JSON.stringify({a:10,b:20})//将object=>string
JSON.parse('{a:10,b:11}')//把字符串变为对象
附加:强制类型转换?
值类型可以进行强制类型转换。
1. 字符串拼接:把数字和bool值转为字符串。
2. == 运算符:
3. if语句判断条件中:都转为bool值
4. 逻辑运算符 && || :把运算符左侧的值转为bool
5.查看变量转为何种bool值:!!变量名
6.其中 0,空字符串'',NaN,null,undefined 都会转为false,而其他都会被转为true undefined is 值类型,null是引用类型
二、原型和原型链-构造函数
题目:
1.如何判断一个变量是数组类型?
var arr = [];
arr instanceo Array//true
typeof arr//数组属于object,用typeof是无法判断是否为数组的
2.写一个原型链继承的例子?
//第一个例子
//animal
function Animal(){
this.eat = function(){
console.log('animal eat')
}
}
//dog
function Dog(){
this.bark = function(){
console.log('dog bark')
}
}
Dog.prototype = new Animal()
//金毛犬
var jinmaoquan = new Dog()
</script>
//第二个例子
function Elem(id){
this.elem = document.getElementById(id);
}
Elem.prototype.html = function(val){
var elem = this.elem;
if(val){
elem.innerHTML = val;
return this;
}else{
return elem.innerHTML;
}
}
Elem.prototype.on = function(type,fn){
var elem = this.elem;
elem.addEventListener(type,fn);
}
var div1 = new Elem('div1');
div1.html('<p>hello imooc</p>');
div1.on('click',function(){
alert('clicked');
})
3.描述new一个对象的过程?
new关键字创建对象的过程:
1.创建一个新的对象;
2.将新创建的对象的原型指向“构造函数的原型”;
3.this指向新创建的对象;
4.返回新创建的对象;function Foo(name,age){ this.name = name this.age = age this.class = "class_class" //return this // 默认有这一行 } var f = new Foo("zhangsan",20)
4.zepto(或其他框架)源码种如何使用原型链?
主要去理解源码把,阅读,不要埋头苦读,zepto源码
知识点:
1.构造函数
1.构造函数的特点:
a:构造函数的首字母必须大写,用来区分于普通函数
b:内部使用的this对象,来指向即将要生成的实例对象
c:使用New来生成实例对象
2.构造函数扩展
构造函数-扩展
var a = {} 其实是 var a = new Object()的语法糖//构造函数为Object
var a = [] 其实是var a = new Array()的语法糖//构造函数是Array
function Foo{...}其实是var Foo =new Function(...)的语法糖//构造函数是Function
使用instanceof判断一个函数是否是一个变量的构造函数(如判断一个变量是否为“数组”,用instanceo Array)
3.原型规则和示例--5个原型规则
1.所有的引用类型(数组,对象,函数),都具有对象特性,即可自由扩展属性(除了“null”以外)
2.所有的引用类型(数组,对象,函数),都有__proto__(隐式原型)属性,属性值是一个普通对象
3.所有的函数,都有一个prototype(显式原型)属性,属性值也是一个普通的对象
4.所有的引用类型(数组,对象,函数),__proto__属性值指向它的构造函数的“prototype”属性值
//1-4 var obj = {};obj.a = 100; var arr = [];arr.a = 100; function fn(){};fn.a = 100; console.log(obj.__proto__); console.log(arr.__proto__); console.log(fn.__proto__); console.log(fn.prototype); console.log(obj.__proto__ === Object.prototype)
5.当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找
//5 //构造函数 function Foo(name,age){ this.name = name; } Foo.prototype.alertName = function(){ alert(this.name); } //创建实例 var f = new Foo('lisi'); f.printName = function(){ console.log(this.name); } //测试 f.printName();//这个对象本身有这个属性 f.alertName();//这个对象本身没有这个属性
4.原型链
首先 对象的隐式原型指向构造函数的显式原型,原型是一个普通对象,所以存在一个原型类,对象不存在的属性到对象的隐式原型去找。
5.instanceof
instanceof是判断引用类型属于那个构造函数 的。
instanceof 的判断逻辑:
f的“__proto__”一层一层往上,能否对应到Foo.prototype ,再试着判断f instanceof Object
附加:判断自身是否有这个属性
var item
for (const key in object) {
//判断是否时自己属性和不是原型属性
if (object.hasOwnProperty(key)) {
const element = object[key];
}
}
三、作用域与闭包
题目:
1.说一下对变量提升的理解?
见下面知识点执行上下文
2.说明this几种不同的使用场景?
见下面知识点this的4个使用场景
3.创建10个<a></a>标签,点击的时候弹出对应的序号
for(var i = 0;i < 10;i++){ var a = document.createElement("a"); a.innerHTML = "love" + i + "<br>"; (function(j){ a.onclick = function(e){ e.preventDefault();//阻止浏览器默认事件,如href的链接跳转 alert(j); } })(i) document.body.appendChild(a);
4.如何理解作用域?
见下面知识点
5.实际开发中闭包的应用?
//闭包实际应用中主要用于封装变量,收敛权限,就是说存入数组里面有没有此项,没有则添加,有则不添加
function isFirstLoad(){
var _list = []
return function(id){
// _list.indexOf(id)返回的是数组的索引
if(_list.indexOf(id) >= 0){
return false
}else{
_list.push(id)
return true
}
}
}
//使用
var firstLoad = isFirstLoad()
console.log(firstLoad(110))//true
console.log(firstLoad(110))//false
console.log(firstLoad(520))//true
知识点:
1.执行上下文
- 范围:一段<script>或者一个函数;
- 全局:变量定义、函数声明;
- 函数:变量定义、函数声明、this、arguments
定义函数有两种方式:函数声明以及函数表达式(函数声明的一个重要特征:函数声明提升)
小tip:区分“函数声明”以及“函数表达式”
函数声明: function fn(){}
函数表达式: var fn = function(){}
例子:
console.log(a)//underfined var a =100 fn("hansen")//"hansen" 22 function fn(name){ age = 22 console.log(name,age) var age }
解释:在全局首先var a 和 function fn先拿出来占位(变量提升)。在函数体中,var age拿出来先占位(变量提升),故函数体里面也不会报错。日常生活中不要这么用。
2.this
1.this要在执行时才能确认值,定义时无法确认
代码:
var a = { name: "long", fn: function () { console.log(this.name);//就是这里有bug 在定义时也无法确定 } } a.fn();// this === a a.fn.call({name: "gong"})// this === {name: "gong"} var fn1 = a.fn; fn1(); // this === window (执行)
2.this 作为构造函数执行,作为对象属性执行, 作为普通函数执行, call apply bind
代码:
//构造函数 function Foo(name){ //this = {}; this.name = name; //return this; } var f = new Foo('zhangsan'); //作为一个对象的属性 var obj = { name : 'A', printName:function(){ console.log(this.name); } } //普通函数的this function fn(){ console.log(this); } fn()// window
//call apply bind function fruit(){ console.log(this.name+'---'+this.color); } var fn1 = {name : '香蕉',color:'黄色'} var fn2 = {ame : '橘子',color : '橙色'} fruit.call(fn1); fruit.apply(fn2); var fn3 = {name : '苹果',color : '红色'} var fruit = function(){ console.log(this.name+'---'+this.color); }.bind(fn3) fruit()
3.作用域
1.无块级作用域
if(true){ var name = "Hansen"; } console.log(name);//Hansen
2.函数和全局作用域
var a = 100 function fn(){ var a = 200 console.log("fn",a) } console.log("global",a) fn() //global 100 //fn 200
补充:ES6是有块级作用域的
//写在花括号里面定义的内容,块级作用域外是不能调用的 if(true){ let a = 10; } console.log(a);//报错:a没有定义
4.作用域链
当前作用域没有定义的变量,即“自由变量”
var a = 100 var c = 300 function fn(){ var b = 200 console.log(a)//自由变量 console.log(b) console.log(c)//自由变量 } fn()
5.闭包
一、一个函数的父级作用域是在它定义的时候的作用域,而非它执行时候的作用域。
二、闭包
1.定义:闭包是一个函数和函数所声明的词法环境的结合。
2.应用场景:模块化、封装。
3.优点:封装性强,使得变量始终保持在内存中。
4.缺点:内存的消耗导致的性能问题。
5.闭包创建:函数嵌套函数,使得内部函数返回出去,让外部来访问内部的变量。
三、闭包的使用场景
1.函数作为一个返回值 。
function F1(){ var a = 100; return function(){ console.log(a); } } var f1 = F1(); var a = 200; f1();
2.函数作为参数传递。
function F1(){ var a = 100 return function(){ console.log(a)//a 是自由变量,向父级作用域寻找 } } var f1 = F1() function F2(fn){ var a = 300 fn() } F2(f1)//100
四、异步与单线程
题目:
1.同步和异步的区别是什么?分别举一个同步与异步的例子
见下面知识点1
2.一个关于setTimeout的笔试题
console.log(1) setInterval(function(){ main.innerHTML += Math.floor(Math.random()*10) + ' '; },1000) console.log(3) setInterval(function(){ main.innerHTML += Math.floor(Math.random()*10) + '* '; },2000) console.log(5) //1 3 5 (然后1000mm的执行一次,然后1000mm和2000mm的同时执行一次,以此类推)
3.前端使用异步的场景有哪些
见下面知识点
知识点:
1.什么是异步(对比同步)
同步会阻塞后续程序代码的执行,而异步不会阻塞程序的运行。
何时需要异步:
1. 在可能发生等待的情况:在等待的过程中程序仍然要执行其他操作。
2. 等待过程中不能像 alert 一样阻塞程序运行。
3.所有的“等待的情况”都需要异步
异步例子:
console.log(100); setInterval(function(){ console.log(200) },1000); console.log(300); //首先他会打印100,由于到了200时发生等待,所以先执行300,最后执行200
同步例子:
console.log(1314); alert("Hansen真帅"); console.log(520); //由于执行时候有alert会卡顿,所以他是同步的
2.前端使用异步的场景
1. 定时任务:setTimeout,setInterval
console.log(100); setInterval(function(){ console.log(200) },1000); console.log(300);
2. 网络请求:ajax 请求,动态 <img> 加载、脚本等文件下载和加载。
console.log('start') $.get('test3.json',function(data){ console.log(data) }) console.log('end') //需要打开apa服务器,test3.json为模拟数据[{ // "a":10, // "b":20 //}, //{ // "c":10, // "d":20 //}]
3. 事件绑定
console.log('start'); document.getElementById('btn1').addEventListener('click',function(){ console.log('ing'); }); console.log('end');
3.异步和单线程
由于js是单线程,在代码执行的时候又不能因为执行需要等待的代码而造成阻塞,因此js会首先将无需等待的(同步)的代码执行完成后,来处理异步的代码,如果达到异步代码的执行条件的话,就会执行。