javascript 学习笔记
1. typeof判断类型
javascript 的原始类型包括:
两个未定义类型:undefined,null;
常用三种:number boolean string
JavaScript的引用类型 object
其中object包括了:function array date
2
问题三:
3.1 如何判断一个变量是数组类型
3.2 写一个原型链继承的例子
3.3 描述 new 一个对象的过程
3.4 zepto(或其他框架)源码中如何使用原型链
背景知识介绍:
// 构造函数 首字母大写 类似于模板 function Foo(name,age){ this.name = name; this.age = age; this.class = 'class-1'; // return this //默认有这一行 }
// 每次new一个类的时候,this先变成空对象,然后在赋值this的name等值,最后在return 出来给new出来的f,则f.name=zhangsan var f = new Foo('zhangsan',20); //创建多个对象 var f1 = new Foo('lisi',22);
所有的引用类型(数组/对象/函数),都具有对象特性,即可以自由扩展属性(除了null以外)如 var obj = {}; obj.a=1000;
所有的引用类型(数组、对象、函数),都有一个 _proto_ (隐式原型)属性,属性值是一个普通的对象。
所有的引用类型(数组、对象、函数),_propto_属性值指向它的构造函数的 “protptype”属性值
其中this指向对象本身。
上面f有三个属性: name,alertName,printName;
为了拿到它自身的属性name,printName:
例如在实例f中调用,本来没有定义过的 f.toString()方法,就按照下图所示,一级一级原型链的往上找:(注意Object的原型是null,避免导致死循环)
instanceOf 用于判断 引用类型 属于哪个 构造函数的方法;
判断f是否输入对象,首先根据原型链找到父级 Foo,无法判断,则再找上一级,一直找到Object
或者把Animal类的方法提取出来:
function Animal(){ } Animal.prototype.eat = function(){ console.log('eat'); } function Dog(){ this.bark = function(){ console.log('dog bark'); } } Dog.prototype = new Animal(); var hashiqi = new Dog(); hashiqi.bark(); hashiqi.eat();
写一个原型继承的例子:
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); return this; } var div1 = new Elem('div1'); div1.html('<p> hello </p>'); div1.on('click',function(){ alert('clicked'); }) /*可以链式操作: div1.html('<p> hello </p>').on('click',function(){ alert('clicked'); })*/
问题四:
4.1 说一下对变量提升的理解
4.2 说明this几种不同的使用场景
4.3 创建10个<a>标签,点击的时候弹出来对应的序号
4.4 如何理解作用域
4.5 实际开发过程中闭包的应用
=======================
4.1.1 执行上下文
范围: 一段<script>或者一个函数
全局:变量定义/函数声明
函数:变量定义,函数声明/this/arguments
console.log(a); //undefined var a = 100; fn('zhangsan'); //'zhangsan' 20 function fn(name){ age = 20; console.log(name,age); var age; }
函数和变量的声明先提取到前面,不同的是变量的提升会用undefined占位,如 var a = undefined 占位,尚未执行到var a = 100 赋值;
而函数声明会把整个函数先提取到前面:在执行到函数 fn(‘zhangsan’)的时候 在看函数内部: 首先提升age变量;
4.2 this要在执行时才能确认值,定义时无法确认:
var a = { name:"A", fn:function (){ console.log(this.name); } } a.fn(); //this === A a.fn.call({name:"B"}) //this === {name:"B"} var fn1 = a.fn; fn1() // this === window
this:
作为构造函数执行:一般在实例化时确认;
作为对象属性执行:类似于上面的例子,一般时对象的属性值;
作为普通函数执行:一般是window
call apply bind:一般是会被改变this的指向;
function Foo(name){ this.name = name;//这里的this表示实例化的f的属性:f.name } var f = new Foo('zhangsan'); ============================= var obj = { name:"aa", printName:function(){ console.log(this.name);//这里的this表示obj } } obj.printName(); ============================= function fn(){ console.log(this); //this===window } fn(); ============================= function fn1(name,age){ alert(name); console.log(this);//下面使用的是call,所以改变了this的指向,指向了call的第一个对象 } //fn1.call({x:1},'zhangsan',20); fn1.apply({x:1},['zhangsan',20]);//apply 和call类似,只不过第二个参数变成了数组 ============================== var fn2 = function(name,age){//bind方法必须是函数表达式的形式 alert(name); console.log(this);//如果不做call applay或bind的操作,这里的this指向window }.bind({y:200});//bind改变this的指向 fn2('zhangsan',20);
作用域:
//无块级作用域,所以外面的name可以拿到这个数据 if(true){ var name = "zhangsan" } console.log(name); ========== //函数和全局作用域 var a = 100;//全局作用域 function fn(){ var a = 200;//函数作用域 console.log('fn',a); } function fn1(){ console.log('fn1',a); } console.log('global',a);//global 100 fn();// fn 200 fn1();// fn1 100
作用域链:自由变量一直往上找
//当前作用域没有定义的变量,即自由变量 //注意的是定义的时候,而不是执行的时刻 var a = 100; function F1(){ var b = 200; function F2(){ var c = 300; console.log(a); console.log(b); console.log(c); } F2(); } F1(); //结果是 100 200 300
{ var a = 100; } console.log(a); //可以获取到a function fn2(){ var a = 100; } fn2(); console.log(a);//获取不到a,是函数内部的局部变量
4.3 创建10个<a>标签,点击的时候弹出来对应的序号
因为变量i放在了全局作用域,每次循环之执行后,都会覆盖之前的值。
而上述方法,每次循环都在一个函数体里,相当于局部作用域,i都是独立的。
5. 异步
5.1 同步和异步的区别是什么?分别举一个同步和异步的例子
5.2 一个关于setTimeout的笔试题;
5.3 前端使用异步的场景有哪些;
(1)定时任务:setTimeout setInterval
(2)网络请求: ajax请求,动态<img>标签
(3)事件绑定
例如:
console.log('start'); var img = document.createElement('img'); img.onload = function (){ console.log('loaded');//图片加载完才执行这里 } console.log('end');
执行顺序为:
start--end--loaded
5.4 获取2017-02-01格式的日期(视频4-5)
5.5 获取随机数,要求是长度一致的字符串格式
5.6 写一个能遍历对象和数组的通用forEach函数
数组API:
forEach 遍历所有元素
every 判断所有元素是否都符合条件
some 判断是否有至少一个元素符合条件
sort 排序
map 对元素重新组装,生成新数组
filter 过滤符合条件的元素
var arr = [1,2,3]; arr.forEach((item,index)=>{ console.log(item); }); ============== var arr = [1,2,3,4]; var result = arr.every((item,index)=>{ //用来判断所有的数组元素,都满足一个条件,只要一个不满足,则返回false if(item<4){ return true } }) console.log(result);//false ============== var arr = [1,2,3,4]; var result = arr.some((item,index)=>{ //用来判断所有的数组元素,只要有一个满足条件即可 if(item<4){ return true } }) console.log(result);//true =============== var arr = [1,4,3,5,2]; var arr2 = arr.sort((a,b)=>{ return a - b;//从小到大排序 return b - a;//从大到小排序 }) console.log(arr2); =============== //map和forEach不同在于可以返回生成一个新的数组 var arr = [1,2,3,4]; var arr2 = arr.map((item,index)=>{ return '<b>'+item+'</b>' }) console.log(arr2); //[ "<b>1</b>", "<b>2</b>", "<b>3</b>", "<b>4</b>" ] ============ //类似的使用forEach则不会返回新数组 var arr = [1,2,3,4]; var arr2 = arr.forEach((item,index)=>{ return '<b>'+item+'</b>' }) console.log(arr); // [1,2,3,4] console.log(arr2);//undefined ============= //类似的使用filter 也可以返回一个新的数组 var arr = [1,2,3,4]; var arr2 = arr.filter((item,index)=>{ if(item>2){ return true; } }) console.log(arr2);//[3,4] ============= //对象api var obj = { x:100, y:200, z:300 } var key; for(key in obj){ if(obj.hasOwnProperty(key)){//这里表示是obj的原生属性而不是集成父级 console.log(key,obj[key]); } }
forEach:
6.1 通用事件绑定
//简单来写 var btn = document.getElementById('btn1'); btn.addEventListener('click',function(event){ console.log('clicked') }) //封装函数的形式 function bindEvent(elem,type,fn){ elem.addEventListener(type,fn); } var a = document.getElementById('link1'); bindEvent(a,'click',function(e){ e.preventDefault();//组织a标签的默认行为 alert('click'); })
6.2 在一个不断动态增加<a>标签的区域,给a上绑定事件,如何做?
<div id="div1"> <a href="#">a1</a> <a href="#">a2</a> <a href="#">a3</a> <a href="#">a4</a> <a href="#">a5</a>
... </div>
使用代理:也就是给包裹a标签的div增减点击的监听事件,如果点击的目标是a则触发事件。这样不用给每一项增加点击事件了。
var div1= document.getElementById('div1'); div1.addEventListener('click',function(e){ var target = e.target; if(target.nodeName === "A"){ alert(target.innerHTML) } })
7 跨域。(所有的跨域请求必须经过信息提供方允许)
可以允许跨域的有三个标签:
<img src="xxx">, <link href="xxx">,<script src="xxx">
JSONP的工作原理: jsonp原理详解——终于搞清楚jsonp是啥了
注意几点:
1. 允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,
这样客户端就可以随意定制自己的函数来自动处理返回数据了。
2. 请求服务端地址,会动态生成一个js文件,包含了一个客户端提供的回调函数。
<script> //这里定义回调函数 window.callback = function (data){ //这是我们跨域得到的信息 console.log(data); } </script> <script src="http://www.coding.com/api.js"></script> <!--上面返回的是 回调函数的 执行 callback({x:100,y:200})-->
也就是在服务器端返回的回调函数 让其执行。在客户端定义改回调函数。
3 服务器端设置可以跨域:
response.setHeader("Access-Control-Allow-Origin","http://a.com,http://b.com"); //允许的域名 或者是* response.setHeader("Access-Control-Allow-Headers","X-Requested-With"); //在服务器端判断request来自Ajax请求(异步)还是传统请求(同步): response.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); //请求方式 response.setHeader("Access-Control-Allow-Credentials","true"); //接受跨域的cookies
8 书写ajax的基本原理;
要完整实现一个AJAX异步调用和局部刷新,通常需要以下几个步骤:
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
var xhr = new XMLHttpRequest(); xhr.open("GET","/api",false); xhr.onreadystatechange = function(){ //onreadystatechange 每次状态改变所触发事件的事件处理程序。 //这里是函数异步执行 if(xhr.readyState == 4){ if(xhr.status == 200){ //status 从服务器返回的数字代码,比如常见的404(未找到)和200(已就绪) alert(xhr.responseText); //responseText 从服务器进程返回数据的字符串形式。 } } } xhr.send(null); //只有在使用send()方法之后,XMLHttpRequest对象的readyState //属性值才会开始改变,也才会激发readystatechange事件,并调用函数。
对于XmlHttpRequest的两个方法,open和send,其中open方法指定了:
a、向服务器提交数据的类型,即post还是get。
b、请求的url地址和传递的参数。
c、传输方式,false为同步,true为异步。默认为true。如果是异步通信方式(true),客户机就不等待服务器的响应;如果是同步方式(false),客户机就要等到服务器返回消息后才去执行其他操作。
我们需要根据实际需要来指定同步方式,在某些页面中,可能会发出多个请求,甚至是有组织有计划有队形大规模的高强度的request,而后一个是会覆盖前一个的,这个时候当然要指定同步方式。
readyState 对象状态值
0 (未初始化) 对象已建立,但是尚未初始化(尚未调用open方法)
1 (初始化) 对象已建立,尚未调用send方法
2 (发送数据) send方法已调用,但是当前的状态及http头未知
3 (数据传送中) 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误,
4 (完成) 数据接收完毕,此时可以通过通过responseXml和responseText获取完整的回应数据
9. cokie 用于存储的缺点
1 存储量很小 只有4kb
2 所有的http请求都会带着,会影响获取资源的效率
3 api简单,需要封装才能使用 document.cookie="name="+username;
10 localStorage 和 sessionStorage
- Html5专门为本地存储而设计,最大容量是5M
- API简单易用 localStorage.setItem(key,value),localStorage.getItem(key)
- 目前所有的浏览器中都会把localStorage的值类型限定为string类型
- localStorage在浏览器的隐私模式下面是不可读取的
- localStorage与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的键值对会被清空
11 js 模块化的好处
比如上面三个js文件 逐层引用;
使用方式:
所以用到模块化:
12
commonjs是用在服务器端的,同步的,如nodejs
amd, cmd是用在浏览器端的,异步的,如requirejs和seajs
其中,amd先提出,cmd是根据commonjs和amd基础上提出的。
12.1 AMD模式:
使用方式:
12.2 CommonJS 规范
CommonJS 属于 node.js 模块化规范,现在被大量用于前端,原因:
1 前端开发依赖的插件和库,都可以从npm中获取;
2 构建工具的高度自动化,使得使用npm的成本非常低;
3 CommonJS 不会异步加载JS,而是同步一次性的加载出来,
需要异步加载js 使用AMD
使用了npm之后建议使用commonJS