JavaScript学习笔记

常说的JavaScript包含两部分:

  1. JavaScript基础知识(ECMA262标准)
  2. JS-Web-API(W3C标准)

JavaScript基础知识

变量类型

undefined
boolean
string
number
function
object
  • 值类型:各个类型都可以明确区分

    console.log(typeof undefined)//undefined
    console.log(typeof true)//boolean
    console.log(typeof 'abc')//string
    console.log(typeof 123)//number
    //console.log(typeof 123.456)//number
    
  • 引用类型:无法明确区分类型,因为函数比较特殊,只能将函数区分出来

    console.log(typeof console.log)//function
    console.log(typeof [])//object
    console.log(typeof {})//object
    console.log(typeof null)//object
    

类型转换

JavaScript是弱语言,存在隐式类型转换。

a = 100+10//110
b = 100+'10'//'10010'

双等与三等

双等只比较二者的值,且存在隐式类型转换;而三等不仅比较二者的值,还会比较它们的类型。

慎用双等,如下所示,表达式100=='100''100'被转换成了数字100…

console.log(100 == '100')//true,100->'100'
console.log(0 == '')//true,0->false;''->false
console.log(null == undefined) //true, null,undefined->false

内置函数与对象

基本内置函数

Object
Array
String
Number
RegExp
Date
Boolean
Function
Error

对象

Math
JSON

构造函数

Q:new一个对象的过程?

A:创建一个对象;this指向这个对象;执行代码,为this赋值;返回this。

//构造函数
function Foo(name, age){
	//this一开始是一个空对象
	this.name = name
	this.age = age
	return this//默认有这一行
}

我们可以通过构造函数定义不同的变量:

var f1 = new Foo('张三', 20)
var f2 = new Foo('李四', 20)

构造函数——扩展

很多对象的构造方法可以简写,如下:

var a = {}//var a = new Object()
var b = []//var b = new Array()
function foo(){}//var Foo = new Function()

Q:怎么判断一个函数是否是一个变量的构造函数?

A:使用instanceof

var a = {}
var b = []
console.log(b instanceof Array)//true
console.log(b instanceof Object)//true

原链规则

  1. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null 除外);
array = []
array.x = 1

obj = Object()
obj.x = 2

function f(){}
f.x = 3
  1. 所有的引用类型(数组、对象、函数),都有一个__proto__属性(隐式原型),属性值是一个普通的对象;
console.log(obj.__proto__)
console.log(array.__proto__)
console.log(f.__proto__)
  1. 所有的函数,都有一个protopyte属性(显式原型),属性值也是一个普通的对象;

  2. 所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的prototype属性值;

console.log(obj.__proto__ === Object.prototype)//true
  1. 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的__proto__(即构造函数的prototype)中查找。
function Hello(name){
	this.name = name
}
Hello.prototype.PrintName = function(){
	console.log(this.name)
}

h = new Hello('tom')
h.PrintName()//tom

原型链

如下代码,h = new Hello('tom')

console.log(h.toString())

查找链为:h.__proto__.__proto__

  1. h对象没有toString属性,根据第五条原型规则,它会去它的__proto__属性中查找,h的构造函数是Hello,所以 h.__proto__等于 Hello.prototype
  2. Hello.prototype也是一个对象,也没有toString属性,所以又会去它的__proto__中查找;
  3. Hello.prototype的构造函数是Object, 所以 Hello.prototype.__proto__等于 Object.prototype
  4. 特例(js为避免死循环):Object.prototype.__proto__等于null

异步与单线程

JavaScript是单线程执行,所以异步非常重要。那么什么时候会需要异步呢?答案是可能发生等待的情况,且等待的时候不能阻塞。

Q:异步与同步的区别。

A:同步会阻塞代码执行,而异步不会;alert是同步,setTimeout是异步。

异步的使用场景

  • 定时任务:setTimeoutsetInverval
  • 网络请求:ajax请求,动态<img>加载;
  • 事件绑定
定时任务

示例1:

console.log(100)
setTimeout(function(){
	console.log(200)
})
console.log(300)
//100 300 200

解析过程:

  1. 执行第一行,打印100
  2. 执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行;即setTimeout是个异步,它会被放在一边(先等着,啥时候有空啥时候执行);
  3. 执行最后一行,打印300;
  4. 待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的任务要执行;
  5. 发现setTimeout中的函数无需等待时间,立即过来执行:打印200。

示例2:

console.log(100)
setTimeout(function(){
	console.log(200)
}, 1000)
console.log(300)
//100 300(1s后) 200

解析过程:

  1. 执行第一行,打印100;
  2. 执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行;因为设置了暂存时间,所有会在1s(1000ms)后解封;
  3. 执行最后一行,打印300;
  4. 待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的任务要执行;
  5. 发现setTimeout中的函数处于封闭状态,等待函数解封后再执行:打印200。
网络请求

示例1:ajax请求

console.log('start')
$.get('https://www.baidu.com/', function(data){
	console.log(data)
})
console.log('end')
//start end data

示例2:动态<img>加载

console.log('start')
var img = document.createElement('img')
img.onload = function(){
	console.log('loaded')
}
img.src = 'x.png'
console.log('end')
//start end loaded
事件绑定
console.log('start')
document.getElementById('js-qa-wenda').addEventListener('click', function(){
	alert('clicked')
})
console.log('end')
//start end (点击后)clicked

作用域与闭包

执行上下文

Q:什么是变量提升?

A:变量定义,函数声明(函数表达式不会)会被提前(即提前至代码首部声明)。

  • 范围:一段script代码或一个函数;
  • 全局:变量定义,函数声明;
  • 函数:变量定义,函数声明,this,arguments。

示例:如下是一段script代码;

<script>
    console.log(a)//undefined
    var a = 100

    func('tom')//tom 20
    function func(name){
        age = 20
        console.log(name, age)
        var age
    }
</script>

全局:在执行第一行之前,执行上下文会将代码块中的变量定义,并赋值为undefined;并把函数声明提前。

函数:变量定义并赋值为undefined,并把thisarguments提前。

作用域

JavaScript没有块级作用域,只有全局和函数作用域;函数作用域不会被函数外部污染,而全局作用域存在变量被覆盖的威胁。

函数与全局作用域
var a=100
function func1(){
	var a = 200
	console.log('func',a)
}
console.log('global',a)//global 100
func()//func 200
自由变量

自由变量:当前作用域没有定义的变量。

var a = 100
function func2(){
	var b=200
	console.log(a)//自由变量绑定:100
	console.log(b)//200
}
func2()

闭包

在JavaScript中,闭包的使用场景有:

  1. 函数作为返回值;
  2. 函数作为参数传递。
function F1(){
	var a=100
	return function(){
		console.log(a)
	}
}
var f1 = F1()
function F2(fn){
	var a=200
	fn()
}
F2(f1)//100

this的使用场景

  1. 作为构造函数执行;
  2. 作为对象属性执行;
  3. 作为普通函数执行;
  4. call apply bind。

示例:call apply bind

//call apply bind
function func3(name){
	console.log(name)
	console.log(this)
}
func3.call({x:100}, 'tom')

func3.call的含义是执行func3函数,并把{x:100}作为this,后面的参数作为func3的参数。

拓展

当函数无定义体时,this全等于window。

function fn(){
	console.log(this)//this === window
}
fn()

数组API

  • forEach:遍历所有元素
  • every:判断所有元素是否都符合条件
  • some:判断是否至少有一个元素符合条件
  • sort:排序
  • map:对元素重新组装,生成新数组
  • filter:过滤符合条件的元素

forEach

var arr = [1,2,3]
arr.forEach(function(item, index){
	console.log(index, item)
})
//0 1
//1 2
//2 3

every

var arr = [1,2,3]
var result1 = arr.every(function(item, index){
	if(item < 2){
		return true
	}
})
console.log(result1)//false

some

var arr = [1,2,3]
var result1 = arr.some(function(item, index){
	if(item < 2){
		return true
	}
})
console.log(result1)//true

sort

var arr1 = [1,4,2,3,5]
var arr2 = arr1.sort(function(a,b){
	//从小到大
	return a-b
	//从大到小
	//return b-a
})
console.log(arr2)//[1,2,3,4,5]

map

var arr3 = [1,2,3,4]
var arr4 = arr3.map(function(item,index){
	return '<b>'+item+'</b>'
})
console.log(arr4)
//["<b>1</b>", "<b>2</b>", "<b>3</b>", "<b>4</b>"]

filter

var arr5 = [1,2,3]
var arr6 = arr5.filter(function(item, index){
	if(item%2===1){
		return true
	}
})
console.log(arr6)//[1,3]

对象API

var obj = {
	x: 100,
	y: 200,
	z: 300,
}
for(var key in obj){
	//原生属性
	if(obj.hasOwnProperty(key)){
		console.log(key, obj[key])
	}
}
//x 100
//y 200
//z 300

JS-Web-API

DOM操作

DOM(Document Object Model),文档对象模型;DOM是一种树型结构,它的本质是HTML结构化(浏览器拿到html代码后负责结构化)之后浏览器和JS可识别的模型;

DOM的常用API有:

  • 获取DOM节点,以及节点的property和Attribute;
  • 获取父/子节点;
  • 新增/删除节点。

Q:DOM节点的attr和property有何区别?

A:property是JS对象的属性(获取和修改);Attribute是html标签的属性(获取和修改)。

示例1:获取DOM节点

var div1 = document.getElementById('div1')//元素
var divlist = document.getElementsByTagName('div')//集合
var containerList = document.getElementsByClassName('.container')//集合

示例2:property(JS对象属性),这些属性是W3C标准规定的。

var pList = document.querySelectorAll('p')//集合
var p = pList[0]//js对象
console.log(p.style.width)//获取样式
p.style.width = '100px'//修改样式
console.log(p.className)//获取class
p.className = 'p1'//修改class
//获取nodeName和nodeType
console.log(p.nodeName)
console.log(p.nodeType)

示例3:Attribute(html标签的属性),如<img data-origin="x.com" src="x.png" style="display:inline">,属性有data-origin,src,style。

var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'imooc')
p.getAttribute('style')
p.setAttribute('style', 'font-size:30px')

示例4:新增/删除节点,获取父/子元素。

//新增节点
var div1 = document.getElementById('div1')
var p1 = document.createElement('p')//添加新节点
p1.innerHTML = 'this is p1'
div1.appendChild('p1')//添加新创建的元素

//获取父元素和子元素
var div1 = document.getElementById('div1')
var parent = div1.parentElement
var child = div1.childNodes

div1.removeChild(child[0])//删除子节点

检测浏览器类型及拆解URL

  • navigator:浏览器(拥有浏览器操作的一些属性)
  • screen:屏幕(获取屏幕的一些属性,如宽和高)
  • location:地址(获取地址栏的一些信息)
  • history:历史(包括前进、后退等)

navigator

//navigator:判断浏览器是否为Chrome
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)

screen

//screen:获取屏幕的宽和高
console.log(screen.width)
console.log(screen.height)

location

//location:对地址栏url的操作
console.log(location.href)//地址栏地址
console.log(location.protocol)//传输协议:http or https
console.log(location.pathname)// 路径名(不包括根),如'/poet/1'
console.log(location.screen)//查询字符
console.log(location.hash)//url中#号后面的就是hash

history

//history:浏览器前进和后退
history.back()//后退
history.forward()//前进

URI编/解码

url = "https://www.baidu.com/?search=你好牛逼"
en_url = encodeURI(url)

console.log(en_url);
//https://www.baidu.com/?search=%E4%BD%A0%E5%A5%BD%E7%89%9B%E9%80%BC
console.log(decodeURI(en_url));
//https://www.baidu.com/?search=你好牛逼

ajax

XMLHttpRequest

无论多复杂的ajax封装,都是基于以下形式而做的:

var xhr = new XMLHttpRequest()//定义xhr对象
xhr.open("GET", "/api", false)//以异步(false为异步,true代表同步)的形式,GET请求访问'/api'页面
xhr.onreadystatechange = function(){
	//此函数是异步执行的
	//每一次readState变化都会触发此函数执行
	if(xhr.readyState == 4){
		if(xhr.status == 200){
			alert(xhr.responseText)
		}
	}
}
xhr.send(null)//发送

发送(send)之后会随时监听状态(xhr.readyState)变化,每发生一次变化就调用一次onreadystatechange

注意:IE低版本使用ActiveXObject,与W3C标准不符;建议忽略。

readyState

readyState是xhr发送请求的状态码,而status是HTTP标准状态码。

  • 0:未初始化(还没有调用send()方法)
  • 1:载入(已调用send()方法,正在发送请求)
  • 2:载入完成(send()方法执行完成,已经接收到全部响应内容)
  • 3:交互(正在解析响应内容)
  • 4:完成(响应内容解析完成,可以在客户端调用了)

跨域

Q:什么是跨域?
A:

​ 浏览器有同源策略,不允许ajax访问其他域接口;

​ 跨域条件:协议、域名、端口,有一个不同就算跨域。

跨域注意事项:

  1. 所有的跨域请求都必须经过信息提供方允许;
  2. 如果未经允许即可获取,那是浏览器同源策略出现漏洞。

可以跨域的三个标签(ajax不允许):

标签示例
img<img src=xxx.img>
css<link href=xxx.css>
js<script src=xxx.js>

例如你的网站要跨域访问慕课网的一个接口,慕课网给你一个地址http://coding.m.imooc.api.js,返回内容格式如callback({x:100, y:200}) (可动态生成)。如下代码:以下代码跨域返回的数据都会被callback接收。

<script>
window.callback = function(data){
	//跨域得到的信息
	console.log(data)
}
</script>
<script src="http://coding.m.imooc.com/api.js" ></script>

Cookie

cookie本身用于客户端和服务器端通信,但是它有本地存储的功能,于是就被“借用”,使用document.cookie即可获取和修改。cookie存储的缺点有:

  • 存储量太小,只有4kb;
  • 所有http请求都会带上cookie,会影响获取资源的效率;
  • API简单,需要封装才能用(cookie中包含多类信息,如账号,密码等)。

localStorage与sessionStorage

localStorage与sessionStorage是HTML5专门为存储而设计,它们都存储在本地,最大容量为5M,且API简洁易用

localStorage.setItem(key, value)
localStorage.getItem(key)

sessionStorage.setItem(key, value)
sessionStorage.getItem(key)

sessionStorage与localStorage的区别:

  1. sessionStorage在浏览器关闭(会话结束)时被清理;
  2. localStorage只要用户不主动清理就会一直存在。

通用事件监听

示例1:addEventListener

var btn = document.getElementById('btn1')
btn.addEventListener('click', function(event){
	console.log('clicked')
})

示例2:

function bindEvent(elem, type, fn){
	elem.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', function(e){
	//此时获取的是超链接,点击默认行为是跳转
	e.preventDefault()//阻止默认行为
	alert(clicked)
})

注意:IE低版本使用attachEvent绑定事件,与W3C标准不一致;建议忽略。

事件冒泡

事件冒泡:节点优先处理绑定到自身的事件,再处理其父辈节点绑定的事件。

有以下html代码:要求点击激活弹出(alert)激活,点击取消弹出取消。

<body>
    <div id="div1">
        <p id="p1">激活</p>
        <p id="p2">取消</p>
        <p id="p3">取消</p>
        <p id="p4">取消</p>
    </div>
    <div id="div2">
        <p id="p5">取消</p>
        <p id="p6">取消</p>
    </div>
</body>

我们可以使用事件冒泡机制来处理:

var p1 = document.getElementById('p1')
bindEvent(p1, 'click', function(e){
	e.stopPropagation()//阻止冒泡
	alert("激活")
})
var body = document.body
bindEvent(body, 'click', function(e){
	alert('取消')
})

e.stopPropagation()很关键:事件冒泡机制中,节点优先处理绑定到自身的事件,再处理其父辈节点绑定的事件。如果不阻止事件冒泡,那么在点击激活后,将先弹出激活(绑定到自身的事件),再弹出取消(绑定到父辈节点的事件)。

代理

如有以下html代码:

<div id="div1">
    <a href="#">a1</a>
    <img src="default.png">
    <a href="#">a1</a>
    <img src="default.png">
    <!--随时新增更多a标签和img标签-->
</div>

使用事件委托(代理)的方式,来处理”点击A标签“事件。

var div1 = document.getElementById('div1')
bindEvent(div1, 'click', function(e){
	e.preventDefault()
	var target = e.target
	if(target.nodeName === 'A'){
		console.log('clicked')
	}
})

页面加载

css/js/img页面加载说明

加载标签说明
css生成CSSOM,不阻塞DOM解析,但阻塞DOM渲染。
js同步加载,阻塞
img异步加载,不阻塞

Q:如何判断页面是否加载完毕?

A:使用window.addEventListenerdocument.addEventListener

window.addEventListener

window.addEventListener('load', function(){
	//页面的全部资源加载完才会执行,包括图片、视频等
})

document.addEventListener

document.addEventListener('DOMContentLoaded', function(){
	//DOM渲染完即可执行,此时图片、视频可能还没加载完
})
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值