出处:慕课网教学视频笔记
https://www.imooc.com/learn/277
书籍介绍:
《javascript权威指南》https://developer.mozilla.org/zh-CN/docs/learn/JavaScript
一、数据类型
- 原始类型:null、undefined、number、string、boolean
- 对象类型:object
- null和undefined
1、 null和undefined都是表示没有的、不存在的值。它们两个在进行逻辑转换时都是false,这两个值进行比较是true。
2、 null表示空引用,它是object类型。undefined表示未定义, 它是undefined类型。
3、 一个变量未定义,或一个变量定义了未赋值,则是undefined,如果用typeof去运算,那它的类型也是undefined。
5、 对说属性来说:如果原来没有这个属性,根本就不存在这个属性,那么它的值就是undefined。
6、 如果这个对象以后要用,但是现在还没有值,一般情况下,会给它一个null值。
7、 在函数(方法)里,如果必须返回值,但是值又计算不出来,那就返回一个null(这是规范,而不是语法规定,JAVASCRIPT里遵循这一规范)
Null 类型是第二个只有一个值的数据类型,这个特殊的值是null。从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用typeof 操作符检测null 值时会返回"object"的原因
- 三种强制转换和两种隐式转换
== 内容一致
===类型,内存地址和内容一致
类型相同,同===
类型不同,尝试类型转换后再比较
- javascript包装对象
- 类型检测
typeof 适合六种基本数据类型
instanceof 判断对象类型比如数组,基于原型链,判断左边操作数对象的原型链是否有右边的构造函数的prototype属性
obj instanceof Object
obj:对象
Object:函数对象,函数构造器
注意:不同window或不同iframe间不能用instanceof
Object.prototype.toString.apply(传要判断的参数)
null的检测建议采用严格===
二、表达式
- 表达式
10*20
[1,2]
{a:1,b:2}
var fn = function(){
}
var obj = {a:1,b:2}
obj.a或者object['a']
调用表达式fn()
- 运算符
in 、new、this 、void
void(0):无论后面是啥都返回undefined
三、语句
- block、var等语句
javascript没有块级作用域
- try-catch-finally
- 函数、switch、循环for...in
- javascript的严格模式
会出错的:with语法、没有声明的变量不能被赋值、delete参数、函数都报错、delete配置属性报错、对象在同一个域内不能定义多个参数、禁止八进制字面量、eval不能变成关键字
typeError
四、对象
- 对象概述
obj.x在自身找得到,obj.z自身找不到,就会往原形上找,直到原型链的末端,没找到就是undefined
- 创建对象、原型链
1、创建对象-字面量
var obj={}
obj.x = 1
2、创建对象-new/原型链
z是从原型继承而来的,不是obj自己的属性
原型不受删除、修改等影响
3、对象创建-Object.create(系统内置函数)
var obj = Object.create({
x:1
})
obj.x // 1
直接是继承原型的,不属于obj自己的属性
原型链的末端是null,也就不包含任何属性和方法了
所以如果var a = Object.create(null),a.toString()是undefined
- 属性操作
1、属性读写异常,如何做判断做保护
var flag = obj&&obj.x&&obj.y(保证每个属性存在才能作为正确条件)
2、属性删除
delete Object.prototype 是false不能删除,不存在
delete person.age 是true(无论是否存在,所以可以重复写删除语句)
总的来说有些属性不允许删除,每一个属性都有一系列标签
这个方法可以查询属性的所有标签,就是属性描述器,是不是可配置
var定义的全局变量、函数等都不可以删除、eval里面定义的可以删除
3、属性检测
判断属性是否存在,或者是否可枚举
- get、set方法
console.log('=========属性get/set方法=========')
var man = {
name:'zhengjiechun',
weibo:'@jiechun',
$age:null,
get age(){
if(this.$age == undefined){
return new Date().getFullYear() - 1993
}else{
return this.$age
}
},
set age(val){
val = +val //利用一元+操作符的特性(会尝试转换为数字),来隐式将一个变量转换为数字类型(number)
if(!isNaN(val)&&val>0&&val<150){
this.$age = +val
}else{
throw new Error('Incorrect val = '+val)
}
}
}
console.log(man.age) //25
man.age = 100
console.log(man.age) //100
//man.age = 'abc' //抛出错误
复制代码
//get set和原型链结合
console.log('=========get set和原型链结合=========')
function foo(){
}
Object.defineProperty(foo.prototype,'z',{
get:function(){
return 1
}
})
var obj = new foo()
console.log('obj.z = >',obj.z)
obj.z = 10
console.log('obj.z = >',obj.z)
Object.defineProperty(obj,'z',{
value:100,
configurable:true, //默认不可配置false,就不可以删除
writable:true //默认不可写false
})
console.log('obj.z = >',obj.z) //100
obj.z = 'slslsl'
console.log('obj.z = >',obj.z) //slslsl设置为可写了
delete obj.z
console.log('obj.z = >',obj.z) //1又找回原型了
复制代码
console.log('=========属性标签=========')
var person = {}
Object.defineProperty(person,'name',{
configuration:false,
writable:false,
enumerable:true, //是否可枚举,就是可否遍历
value:'zhengjiechun'
})
Object.defineProperty(person,'age',{
configuration:false,
writable:false,
enumerable:false, //是否可枚举,就是可否遍历
value:'zhengjiechun'
})
//设置多个属性的
Object.defineProperties(person,{
title:{
configuration:false,
writable:false,
enumerable:true, //是否可枚举,就是可否遍历
value:'测试多属性的defineProperties'
},
salary:{
configuration:false,
writable:true,
enumerable:true, //是否可枚举,就是可否遍历
value:'13000'
},
luck:{
get:function(){
return Math.random()>0.5?'good':'bad'
}
},
promoto:{
set:function(level){
this.salary*=1+level*0.1
}
}
})
console.log('测试枚举=>',Object.keys(person)) //获取对象上的属性
console.log('查看name有哪些属性标签=>',Object.getOwnPropertyDescriptor(person,'title'))
console.log('原来的salary=>',person.salary) //13000
person.promoto = 2
console.log('后面的salary=>',person.salary) //15600复制代码
对象标签/对象序列化
[[proto]]、[[class]]、[[extensible]]
原型、类、是否可扩展
isFrozen是否被冻结
序列化、其他对象的方法
stringify()
解析序列化后的,转成json:JSON.parse()
序列化-自定义
/对象标签
console.log('=========对象标签=========')
console.log('=========序列化-自定义=========')
var objj = {
x:1,
y:2,
o:{
o1:1,
o2:2,
toJSON:function(){
return this.o1 + this.o2
}
}
}
console.log('序列化-自定义-toJSON方法是固定的=>',JSON.stringify(objj))//{"x":1,"y":2,"o":3}
复制代码
其它对象方法-可自定义
console.log('=========其他对象方法=========')
var objjj = {a:1,b:2}
console.log('其他对象方法=>',objjj.toString())
objjj.toString = function(){
return this.a+this.b
}
console.log('其他对象方法=>',objjj.toString()) //3
objjj.otherMethod = function(){
return this.a+this.b+100
}//自定义方法
console.log('其他对象方法=>',objjj.otherMethod()) //103
复制代码
五、数组
六、函数和作用域(函数、this)
- 函数概念
函数名、参数列表、函数体、一次定义,多次执行和调用
this/argument/作用域/不用调用方式/不同创建方法
不同调用方式
- 函数声明及表达式(创建函数的两种函数)
区别:函数声明会被前置,函数表达式只提前var add
命名函数表达式(不怎么用)是可以匿名的
var fn = function(){
}
var fn = function fnn(){
}
Function构造器(不怎么用)是匿名的
- this
全局this,就是window
console.log('===========this的使用==========')
this.a = 36
console.log(window.a) //36
console.log(this === window) //true
console.log(this.document===document) //true
复制代码
七、函数和作用域(闭包、作用域)
- 闭包的例子
//闭包的例子
function outer(){
var localvalue = 30
return function(){
return localvalue
}
}
var func = outer()
console.log(func())
function outer(){
var localvalue = 30
return localvalue
}
//var func = outer() //报错,func不是一个函数
//console.log(func())
!function(){
var localDate = "localData here"
document.addEventListener('click',function(){
console.log(localDate)
})
}()//输出localData here,前面加了!是变成函数表达式,后面才可以加()直接执行
复制代码
- 闭包-循环常见错误
// for(var i=1;i<4;i++){
// document.getElementById('div'+i).addEventListener('click',function(){
// alert(i) //都是4,每一次点击的时候i都已经初始化完成后
// })
// }
for(var i=1;i<4;i++){
!function(i){
document.getElementById('div'+i).addEventListener('click',function(){
alert(i) //运用1,2,3
})
}(i)
}
复制代码
- 闭包-封装
(function(){
var id = 1223;
var name = 'zjc';
var exports = {};
var like = 'sss'
exports.age = 12
function converter(id){
return +id;
}
exports.getId = function(){
return converter(id);
}
exports.getName = function(){
return name;
}
exports.getLike = function(){
return like;
}
exports.setLike = function(_like){
like = _like
return like
}
window.exports = exports; //暴露给window
}());
console.log(exports.getId()) //1223
console.log(exports.getName()) //zjc
exports.age = 13
console.log(exports.age) //13
console.log(exports.getLike()) //sss
exports.setLike('skskkld')
console.log(exports.getLike()) //skskkld
复制代码
总之:
优点:灵活、方便、封装
缺点:空间浪费、内存泄露、性能消耗
- 作用域
全局作用域、函数、eval
函数有自己的独立作用域,比如函数内部变量
作用域链:从内向外
- es3执行上下文
八、OOP上
面向对象编程
- 概念与继承
继承、封装、多态、抽象
1、基于原型的继承
//一个函数对象的结构:属性
function Foo(){
}
Foo.prototype.x = 1
var obj = new Foo()
//结构如下,prototype指Foo的对象属性,指向Object,
//用于new出来的对象obj的原型,prototype和原型是两回事
// Foo.prototype
// {
// constructor:Foo,
// _proto_:Object.prototype,//原型,方法有toSring()、valueOf等
// x:1
// }
复制代码
prototype是new出来的实例obj1、obj2、obj3的原型,如果要实例共享的方法,可以写在原型对象上添加共用的属性和方法
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.hi = function(){
console.log('Hi,的的名字是'+this.name + ',我的年龄是'+this.age)
}
Person.prototype.legs = 2
Person.prototype.arms = 2
Person.prototype.walk = function(){
console.log(this.name + "正在走路")
}
function Student(name,age,className){
Person.call(this,name,age)//调父类
this.className = className
}
//让student实例继承person.prototype上的一些方法,Student和Person执行同一个原型,也可以各自添加自己的方法和属性,
//不用Student.prototype = Person.prototype
Student.prototype = Object.create(Person.prototype)//创建一个原型为Person.prototype的空对象,对象的原型指向一个参数
Student.prototype.constructor = Student
Student.prototype.hi = function(){
console.log('Hi,我的名字是'+this.name + ',我的年龄是'+this.age+',来自'+this.className)
}
Student.prototype.learn = function(subject){
console.log(this.name + '正在'+this.className+'学习' + subject)
}
var zjc = new Student('郑洁纯',25,'计科二班')
zjc.hi()//Hi,我的名字是郑洁纯,我的年龄是25,来自计科二班
console.log(zjc.legs)//2
zjc.walk()//郑洁纯正在走路
zjc.learn('数学')//郑洁纯正在计科二班学习数学复制代码
再谈原型链- prototype属性
1、改变prototype
2、内置函数构造器的prototype
3、创建对象-new/原型链
- instanceof
- 实现继承的方式
九、OOP下
- 模拟重载
function Person(){
var args = arguments
//null的类型也是object要排除
if(typeof args[0] === 'object'&&args[0]){
//参数为对象的情况
if(args[0].name){
this.name = args[0].name
}
if(args[0].age){
this.age = args[0].age
}
}else{
if(args[0]){
this.name = args[0]
}
if(args[1]){
this.age = args[1]
}
}
}
Person.prototype.toString = function(){
return 'name='+this.name +',age='+this.age
}
var zjc = new Person('zjc',25)
console.log(zjc.toString())//OOP模拟重载、链式调用、模块化.html:35 name=zjc,age=25
var nunn = new Person({name:'nunn',age:23})
console.log(nunn.toString())//name=nunn,age=23
复制代码
根据参数类型或者数量的区别去让同样的函数名字可以根据不同参数列表的情况去调用
- 调用子类的方法
1、call()和apply()
call, apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例,也就是每个方法都有call, apply属性.既然作为方法的属性,那它们的使用就当然是针对方法的了.这两个方法是容易混淆的,因为它们的作用一样,只是使用方式不同.
相同点:两个方法产生的作用是完全一样的
不同点:方法传递的参数不同
当参数明确时可用call, 当参数不明确时可用apply给合arguments
2、基类和子类
//调用子类方法,apply和call
function per(name){
this.name = name
}
function stu(name,className){
this.className = className
per.call(this,name)//子类调用基类
}
var bosn = new stu('bosn','math')
console.log(bosn)
复制代码
3、链式调用
//链式调用
function classManager(){
}
classManager.prototype.addClass = function(str){
console.log('class:'+str+' added')
return this
}
var manager = new classManager()
manager.addClass('classA').addClass('classB').addClass('classC')
//class:classA added
//class:classB added
//class:classC added
复制代码
4、抽象类
5、es5的defineProperty()
seal取消对property的扩展
create
6、模块化
//模块化
var moduleA
moduleA = function(){
var prop = 1
//内部的方法或变量不会泄露到外层,外层就放最核心的东西放全局作用域就行
function func(){
}
return{
func:func,
prop:prop
}
}()
console.log(moduleA)
var moduleB
moduleB = new function(){
var prop = 1
function func(){
}
this.func=func
this.prop=prop
}
console.log(moduleB)
复制代码