[JavaScript]JS基础知识(上)
变量类型及计算
变量类型
值类型 vs 引用类型
值类型:
var a = 100
var b = a
a = 200
console.log(b) //100
引用类型:
var a = {age:20}
var b = a
b.age = 21
console.log(a.age) //21
引用类型:对象、数组和函数
特点:无限扩展属性,内存占用可能过大,故共用内存空间以节省空间
Typeof运算符详解
typeof undefined //undefined
typeof 'abc' //string
typeof 123 //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function
注:null也是对象
1~4行是值类型,5~8行是引用类型,但JS只能区分函数与非函数,而不能区分数组和对象。
变量计算
强制类型转换
字符串拼接
var a = 100 + 10
var b = 100 + '10'
console.log(a) //110
console.log(b) //10010
数字 + 字符串时将数字自身转为字符串。
==运算符
console.log(100 == '100') //true
console.log(0 == '') //true
console.log(null == undefined) //true
==计算会试图让前后的比较对象相等,故发生类型转换
if语句
var a = true
if(a){
console.log('a');
}
var b = 100
if(b){
console.log('b')
}
var c = ''
if(c){
console.log('c')
}
//输出 a
// b
//(c不输出)
100被强制转换为true,而’’被转换为false
逻辑运算
console.log(10 && 0) //0
console.log('' || 'abc') //abc
console.log(!window.abc) //true
//判断一个变量会被当成true 还是 false
var a = 100
console.log(!!a) //true
题目:
1、何时使用 === 何时使用 == ?
if(obj.a == null){
//相当于 obj.a === null || obj.a === undefined 的简写形式
//jQuery源码推荐写法
}
除了这种情况外,全部使用===
2、JS中有哪些内置函数
- Object
- Array
- Boolean
- Number
- String
- Function
- Date
- RegExp
- Error
3、如何理解JSON?
Json既是数据格式,也是数据对象。
JSON.stringify({a:10,b:20})
JSON.parse('{"a":10,"b":20}')
原型和原型链
构造函数
function Foo(name,age){
this.name = name
this.age = age
this.class = 'class-1'
//return this //默认有这一行
}
var f = new Foo('zhangsan',20)
//var f1 = new Foo('lisi',21) //创建多个对象
构造函数需使用大写开头!
扩展:
var a = {} 其实是 var a = new Object() 的语法糖
Var a = [] 其实是 Var a = new Array() 的语法糖
Function Foo(){…} 其实是 var Foo = new Function(…)的语法糖
使用instanceof判断一个函数是否是一个变量的构造函数
判断一个变量是否为“数组”: 变量 instanceof Array
5条原型规则
1、所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了null以外)
var obj = {};obj.a = 100;
var arr = [];arr.a = 100;
function fn(){}
fn.a = 100
2、所有的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象。(隐式原型)
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
输出结果:
3、所有的函数,都有一个prototype属性,属性值也是一个普通的对象(显式原型)
console.log(fn.prototype)
输出结果 4、所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的“prototype”属性值
console.log(obj.__proto__ === Object.prototype) //true
5、当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找
function Foo(name, age){
this.name = name
}
Foo.prototype.alertName = function(){
alert(this.name)
}
//创建示例
var f = new Foo('zhangsan')
f.printName = function(){
console.log(this.name);
}
//测试
f.printName()
f.alertName()
this:
永远指向f自身,故能读取属性
循环对象自身的属性
var item
for (item in f){
//高级浏览器已经在for in中屏蔽了来自原型的属性
//但是建议还是加判断
if(f.hasOwnProperty(item)){
console.log(item)
}
}
原型链
function Foo(name, age){
this.name = name
}
Foo.prototype.alertName = function(){
alert(this.name)
}
//创建示例
var f = new Foo('zhangsan')
f.printName = function(){
console.log(this.name);
}
//测试
f.printName()
f.alertName()
f.toString() // 要去f.__proto__.__proto__中查找
注意,toString()是Object.prototype中的方法,f通过读取__proto__的方式寻找该方法,但它的上一级原型并没有这个方法,故寻找隐式原型的隐式原型,最终在Object中找到了toString()方法。
Instanceof的用法介绍
用于判断引用类型属于哪个构造函数的方法
F instanceof Foo 的判断逻辑是:
F 的 proto 一层一层往上,能否对应到 Foo.prototype
再试着判断 f instanceof Object
题目:
1、如何准确判断一个变量是数组类型
var arr = []
arr instanceof Array // true
typeof arr //Object
2、写一个原型链继承的例子
封装DOM查询:
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')
//console.log(div1.html())
div1.html('<p>hello world</p>')
div1.on('click',function(){
alert('clicked')
})
//链式操作
div1.html('<p>hello world</p>').on('click',function(){
alert('clicked')
})
3、描述new一个对象的过程
function Foo(name,age){
this.name = name
this.age = age
this.class = 'class-1'
//return this //默认有这一行
}
var f = new Foo('zhangsan',20)
- 创建一个新对象
- this指向这个新对象
- 执行代码,即对this赋值
- 返回this
[JavaScript]JS基础知识(中)
作用域和闭包
执行上下文
范围:一段<script>或者一个函数
全局:变量定义、函数声明(一段<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
bar(100)
function bar(num){
console.log(num)
}
}
在第2行执行之前,a被声明为undefined,而在第5行执行时,函数fn直接被提前完整声明了,所以可以正确执行。
在第6~15行,是函数内的执行上下文,同理,在第8行中读取age的属性,自动为age完成了“var age”的声明,故程序可正常执行。
建议定义要放前面,方便他人阅读。
在函数fn中,bar(100)也会去寻找function bar()的声明,故也能正常输出数字。
关于this、argument,在函数执行之前,this就已经确定了值。
注意“函数声明”和“函数表达式”的区别
//函数声明
function fn(){
//声明
}
//函数表达式
var fn = function(){
//表达式
}
注意,执行第1行时,如果是用函数声明的方式定义函数,则可以执行,而使用函数表达式的方式,则报错,因为找不到函数的定义。
this
作为构造函数执行:
function Foo(name){
this.name = name
}
var f = new Foo("zhangsan")
作为对象属性执行:
var obj = {
name: 'A',
printName: function(){
console.log(this.name)
}
}
obj.printName()
作为普通函数执行:
function fn(){
console.log(this) //this === window
}
fn()
call apply bind:
function fn1(name,age){
alert(name)
console.log(this)
}
fn1.call({x:100},'zhangsan',20)
fn1.apply({x:100},['zhangsan',20])
var fn2 = function(name,age){
alert(name)
console.log(this)
}.bind({y:200})
fn2('zhangsan',20)
call、apply和bind可以修改this的值。其中要注意bind必须要用函数表达式的方式才能执行。
作用域
js中,无块级作用域:
if(true){
var name = 'zhangsan'
}
console.log(name) //zhangsan
外部可以访问if语句块中的name
有函数和全局作用域:
var a = 100
function fn(){
var a = 200
console.log('fn',a) //fn 200
}
console.log('global',a) //global 100
fn()
由于有函数作用域和全局作用域,故两次输出a的值不同。定义到函数里的变量,外部不可调用、改变。
作用域链
关于自由变量:
var a = 100
function fn(){
var b = 200 //当前作用域中没有定义的变量,即“自由变量”
console.log(a)
console.log(b)
}
fn()
在fn函数中,并没有定义a,但是fn从其父级作用域中寻找变量。
函数的作用域是由函数定义的时候指定的,而不是函数执行时指定的。
关于作用域链:
var a = 100
function f1(){
var b = 200
function f2(){
var c = 300
console.log(a) //自由变量
console.log(b) //自由变量
console.log(c)
}
f2()
}
f1()
F1的父级作用域是全局作用域,f2的父级作用域是f1,因此当打印a时,js向父级作用域中寻找,而f1作用域没有,则再往f1的父级作用域,即全局作用域中寻找,再全局作用域中找到a,而b也同理。
这个寻找的过程是从作用域链中一步步寻找得到的,这就是作用域链。
闭包
函数作为返回值的情况:
function F1(){
var a = 100
//返回一个函数(函数作为返回值)
return function(){
console.log(a)
}
}
//f1得到一个新函数
var f1 = F1()
a = 200
f1() // 10
F1函数返回了一个函数。将返回的函数赋值给了f1,之后执行f1。
在第5行中,a是自由变量,故到它的父级作用域中寻找,而F1()函数中定义了a = 100,所以a的值为100。
函数作为参数传递的情况:
function F1(){
var a = 100
//返回一个函数(函数作为返回值)
return function(){
console.log(a)
}
}
var f1 = new F1()
function F2(fn){
a = 200
fn()
}
F2(f1); //还是 100
题目:
1、说一下对变量提升的理解:
变量定义 函数声明(注意和函数表达式的区别)
2、说明this几种不同的使用场景
作为构造函数执行 作为对象属性执行 作为普通函数执行 Call apply bind
3、创建10个<a>标签,点击的时候弹出来对应的序号
错误代码:
var i,a
for(i = 0;i < 10;i++){
a = document.createElement('a')
a.innerHTML = i + '<br/>'
a.addEventListener('click',function(e){
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
执行结果:无论点击哪个链接,都弹出10。原因是在全局作用域中,i在循环结束时的值为10,所以不管之前定义时i的值为多少,总是返回当前作用域的i的值。
正确解法:使用自执行函数(就是不用调用,只要定义完成,立即执行的函数)
正确代码:
var i
for(i = 0;i < 10;i++){
(function(i){
//存在自己的函数作用域
var a = document.createElement('a')
a.innerHTML = i + '<br/>'
a.addEventListener('click',function(e){
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
})(i)
}
结果截图:
[JavaScript]JS基础知识(下)
异步和单线程
异步
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
//输出:
//100
//300
//200(过了1秒后)
同步
console.log(100)
alert(200)
console.log(300)
//在弹窗弹出200时,控制台是不打印300的,直到弹框结束才打印300
同步会阻塞下面代码的执行,什么时候消除阻塞,才会继续往下走。
异步不会阻塞,只是继续等待。
异步同步的差异在于,会不会阻塞后续代码的执行。
何时需要异步?
在可能发生等待的情况。
等待过程中不能像alert一样阻塞程序执行。
因此,所有的“等待的情况”都需要异步。
前端使用异步的场景
定时任务:setTimeout(设置多少毫秒之后执行函数体),setInterval(设置每多少毫秒之后执行函数体)
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
//输出:
//100
//300
//200(过了1秒后)
网络请求:ajax请求:
console.log('start')
$.get('./data1.json',function(data1){
console.log(data1)
})
console.log('end')
动态<img>加载:
console.log('start')
var img = document.createElement('img')
img.onload = function(){
console.log('loaded')
}
img.src = 'img/HBuilder.png'
console.log('end')
事件绑定:
console.log('start')
document.getElementById('div1').addEventListener('click',function(){
alert('clicked')
})
console.log('end')
异步和单线程
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
//输出:
//100
//300
//200(过了1秒后)
执行第1行,打印100,
执行setTimeout后,传入setTimeout函数会被暂存起来,不会立即执行(单线程的特点,不能同时干两件事),
执行最后一行,打印300,
待所有程序执行完,处于空闲状态时,会马上查看有没有暂存起来的要执行的函数,
发现暂存起来的setTimeout中的函数无需等待时间,就立即发过来执行。
题目
1、同步和异步的区别是什么?分别举一个同步和异步的例子。
同步会阻塞代码执行,而异步不会。
Alert是同步,setTimeout是异步。
2、一个关于setTimeout的笔试题:
console.log(1)
setTimeout(function(){
console.log(2)
},0)
console.log(3)
setTimeout(function(){
console.log(4)
},1000)
console.log(5)
输出:1 3 5 2 4
3、前端使用异步的场景有哪些?
都需要等待,单线程,为了不阻塞,只能等待。
其他知识
日期
Date.now() //当前时间的毫秒数
var dt = new Date()
dt.getTime() //获取毫秒数
dt.getFullYear() //年
dt.getMonth() //月(0~11)
dt.getDate() //日(0~31)
dt.getHours() //小时(0~23)
dt.getMinutes() //分钟(0~59)
dt.getSeconds() //秒 (0~59)
Math
获取随机数Math.random()
数组API
forEach 遍历所有元素:
var arr = [1,2,3]
//参数为 元素,下标,元素在前,下标在后
arr.forEach(function(item, index){
//遍历数组中的所有元素
console.log(index,item)
})
every 判断所有元素是否都符合条件:
var arr = [1,2,3]
var result = arr.every(function(item, index){
//用来判断所有的数组元素,是否都满足一个条件
if(item < 2){
return true
}
})
console.log(result) //false
some 判断是否有至少一个元素符合条件:
var arr = [1,2,3]
var result = arr.some(function(item, index){
//用来判断所有的数组元素,只要有一个满足条件即可
if(item < 2){
return true
}
})
console.log(result) //true
sort 排序
var arr = [1,4,2,3,5]
var arr2 = arr.sort(function(a,b){
//从小到大排序
return a - b
})
console.log(arr2)
map 对元素重新组装,生成新数组
var arr = [1,2,3,4]
var arr2 = arr.map(function(item,index){
//将元素重新组装,并返回
return '<b>' + item + '</b>'
})
console.log(arr2)
filter 过滤符合条件的元素:
var arr = [1,2,3]
var arr2 = arr.filter(function(item,index){
//通过某一个条件过滤数组
if(item >= 2){
return true
}
})
console.log(arr2)
对象API
for in
var obj = {
x:100,
y:200,
z:300
}
var key
for(key in obj){
if(obj.hasOwnProperty(key)){
console.log(key,obj[key])
}
}
题目
1、获取2017-06-10格式的日期
function formatDate(dt){
if(!dt){
dt = new Date()
}
var year = dt.getFullYear()
var month = dt.getMonth() + 1
var date = dt.getDate()
if(month < 10){
//强制类型转换
month = '0' + month
}
if(date < 10){
//强制类型转换
date = '0' + date
}
//强制类型转换
return year + '-' + month + '-' + date
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)
2、获取随机数,要求是长度一致的字符串格式
var random = Math.random()
random = random + '0000000000'//后面加上10个零
random = random.slice(0,10)
console.log(random)
3、写一个能遍历对象和数组的通用forEach函数
//对数组:
var arr = ['a','b','c']
forEach(arr,function(item,index){
console.log(index,item)
})
//对对象:
var obj = {
'one':'a',
'two':'b'
}
for(key in obj){
console.log(key,obj[key])
}
//组合后:
function forEach(obj,fn){
var key
if(obj instanceof Array){
//准确判断是不是数组
obj.forEach(function(item,index){
fn(index,item)
})
}else{
//不是数组就是对象
for(key in obj){
fn(key,obj[key])
}
}
}
[JavaScript]JS WEB API(上)
JS WEB API
Ecma262 + w3c
DOM操作
1、DOM是哪种基本的数据结构?树
2、DOM操作的常用API有哪些?
获取DOM节点,以及节点的property和attribute
获取父节点,获取子节点
新增节点,删除节点
3、DOM节点的attr和property有何区别?
Property只是一个js对象的属性的修改
Attribute是对html标签属性的修改
DOM本质
浏览器把拿到的HTML代码,结构化一个浏览器能识别并且js可操作的一个模型而已。
DOM节点操作
获取DOM节点
prototype 获取的是js对象 attribute 获取的是DOM节点
DOM结构操作
新增节点 获取父元素、获取子元素、删除节点
BOM操作
Navigator、Screen Location、History
1、如何检测浏览器的类型?
Navigator
2、拆解url的各部分
location