变量类型和数据类型:
什么是数据,就是‘abc’ {} [] true 类似的数据
什么变量:我们创建的代表数据的一个标识符
var a = 1; //变量a是基本类型 1是基本数据类型
var b = [1, 2]; //变量b是引用类型 [1,2]是对象类型
返回当前数据的类型字符的字符串
typeof有两种用法 typeof(value) / typeof value
可以检测的类型
console.log(log); // ƒ log() { [native code] }
var log = console.log;
log(typeof 123); //number
log(typeof "123"); //string
log(typeof (null)); //object
log(typeof undefined); //undefined
log(typeof []); //object
log(typeof {}); //object
log(typeof true); //boolean
log(typeof function () {}); //Function
log(typeof Math); //Object Math直接就是对象,不需要实例化才运行
log(typeof Date); //Function
log(typeof new Date()); //Object
如果B的显式原型对象 在 A的原型链上(通过__proto__确定的) 则返回true
A instanceof B:
B的原型对象在A的原型链上
主要用来检测对象类型,对于基本类型来说不能检测
可以检测的类型是:Array/Function/RegExp/Object
log([] instanceof Array); //true
log([] instanceof Object); //true
log(Array instanceof Function); //true
log([] instanceof Function); //false
log({} instanceof Array); //false
log("123" instanceof String); //false
log(1 insNumber); //false
log(true instanceof Boolean); //false
log(/\s/g instanceof RegExp) //true
log(new String("123") instanceof String); //true
因为null和undefined都只有一个唯一的值,null和undefined
所以可以通过全等 === 来检测这两个类型
var a = null;
var b;
log(b === undefined) //true
log(a === null) //true
var a = null;
console.log(a === null); //true
其他的类型也有toString方法,但是和Object对象上的toString方法得到的结果不同
Object上的toString方法可以得到[object XXXX]的一个字符串 可以精确的确定对象的类类型
Object.prototype.toString.call(被检测的值)
console.log(console.log(1));
//1 undefined
在控制台打印的1 undefined 因为log函数没有返回值
声明一个a是一个对象
在堆中创建一个对象
在栈中创建一个变量
把对象的地址赋值给栈中的变量
如果对象不用了,那么我们需要把对象给从内存中删除,这样节省空间,如果a是局部作用域的变量,那么函数执行完成的时候,a就会自动销毁,a销毁以后,对象就没有被变量所引用(此时对象已经不可能再被使用),此时对象就会变成垃圾对象,垃圾对象会等待回收
但是如果包含对象引用地址的变量是全局变量,则全局变量只有等待浏览器关闭才能销毁.所以此时不用的对象一直被引用,会占用空间.把变量主动设置为null,对象就没有被引用了,就变成了垃圾对象
内存的生命周期
生命周期:一个东西 从出生到死亡的全过程
内存:通电 – 内存生效保存数据 – 断电(数据全部清空)
内存创建的时候:
堆空间:存放一些对象类型(大)
栈空间:存放基本类型和变量和地址值 (小)
内存要区分开:
运行内存和硬盘内存
变量就是代表内存的一块区域,通过变量就可以访问到这个区域
是由一系列的键值对(key value)组成
一定是一个字符串
可以是任意类型的,其中如果值是function的话,对应的属性被称作为方法
会把 点 后的值当做字符串解析 ,点后应该直接跟对象的属性
[]操作符(通用的):中括号中可以运算及直接解析变量,也可以直接书写对象的属性(必须是字符串格式)
1.当key值是一个变量的时候
2.当key值是一个不规则字符的时候
var car1 = new Object();
var car1 = {};
car1.color = "red";
car1.speed = "300";
car1.name = "BMW-X3";
car1.run = function () {
console.log("最高时速" + this.speed);
}
function car(name, color, speed) {
var o = {};
o.name = name;
o.color = color;
o.speed = speed;
o.run = function () {
console.log("最高时速" + this.speed);
}
return o;
}
var car1 = car("BMX-X1", "red", 300);
var car2 = car("BMX-X3", "red", 330);
console.log(car1 instanceof car); //false
console.log(car1 instanceof Object); //false
console.log(car1.constructor); //Object
function Car(name, color, speed) {
//如果当前的函数被new调用了,当前函数就是一个构造函数
//构造函数中的this指向其实例化对象
//构造函数不需要先创建一个对象,因为实例化的时候本身就创建了一个对象
this.name = name;
this.color = color;
this.speed = speed;
this.run = function () {
console.log("最高时速" + this.speed);
}
}
var car1 = new Car("BMX-X1", "red", 300);
var car2 = new Car("BMX-X3", "red", 330);
console.log(car1, car2);
console.log(car1.run === car2.run) //false
function Car() {
}
Car.prototype.color = "red";
Car.prototype.name = "BMX-X1";
Car.prototype.speed = "200";
Car.prototype.run = function () {
console.log("最高时速" + this.speed);
}
var car1 = new Car();
var car2 = new Car();
console.log(car1.run === car2.run)
function Car(name, color, speed) {
this.name = name;
this.color = color;
this.speed = speed;
}
Car.prototype.run = function () {
console.log("最高时速" + this.speed);
}
var car1 = new Car("BMX-X1", "red", 300);
var car2 = new Car("BMX-X3", "red", 330);
console.log(car1 instanceof Car); //true
console.log(car1.constructor); //Car
console.log(car1.run === car2.run); //true
function Car(color, speed) {
//私有属性
var num = 0;
//私有方法
function fn() {
console.log("fn")
}
//这个属性是设置给所有实例化对象的(公有属性)
this.color = color;
this.speed = speed;
//在构造函数中给实例化对象设置的方法被称作为 特权方法
this.getColor = function () {
console.log("特权方法")
}
}
//这个方法是设置给所有实例化对象的(公有方法)
Car.prototype.run = function () {
console.log("驾~")
}
//就是简单的把Car当做一个对象(函数对象),扩展了属性和方法
//这些属性和方法被称作为静态属性和静态方法
Car.title = "造车";
Car.do = function () {
console.log("doit")
}
面向对象编程三大特性
对于封装而言,一个对象它所封装的是自己的属性和方法,所以它是不需要依赖其他对象就可以完成自己的操作。
1、良好的封装能够减少耦合。
2、类内部的结构可以自由修改。
3、可以对成员进行更精确的控制。
4、隐藏信息,实现细节。
当一个对象能够使用另一个对象的属性和方法的时候,被称作为继承
var obj1 = {
name: "lily",
do: function () {
console.log("eat")
}
}
var obj2 = Object.create(obj1);
obj2.sex = "nv"
//obj2可以使用obj1的属性和方法,称作为继承
console.log(obj2, obj1)
console.log(obj2.name)
只能继承构造函数中添加的属性和方法
1. 定义父类型构造函数
2. 定义子类型构造函数
3. 在子类型构造函数中调用父类型构造(call)
/1.借用构造函数继承:只能继承构造函数中添加的属性和方法
//封装一个父类
function Animal(name, age) {
this.name = name;
this.age = age;
}
Animal.prototype.say = function () {
cosnole.log("吼~")
}
//封装一个子类
function Cat(color, name, age) {
//本来Animal的this指向的是Animal的实例化对象
//现在让Animal的this指向Cat的实例化对象
//并且给Animal传参
Animal.call(this, name, age)
this.color = color;
}
Cat.prototype.do = function () {
console.log("玩红外线");
}
var cat1 = new Cat("white", "baozi", 2);
var cat2 = new Cat("coffce", "wanan", 3);
console.log(cat1, cat2)
1. 定义父类型构造函数
2. 给父类型的原型添加方法
3. 定义子类型的构造函数
4. 创建父类型的对象赋值给子类型的原型
5. 将子类型原型的构造属性设置为子类型
6. 给子类型原型添加方法
7. 创建子类型的对象: 可以调用父类型的方法
function Animal(name, age) {
this.name = name;
this.age = age;
}
Animal.prototype.say = function () {
cosnole.log("吼~")
}
var ani = new Animal();
ani.do = function () {
}
function Cat(color, name, age) {
this.color = color;
}
//Cat的实例化对象想要使用Animal的原型对象上的方法,需要修改Cat的原型链
//千万不要把Animal的原型对象直接赋值给Cat的原型对象,否则修改Cat Animal也修改了,因为赋值是地址值的赋值
// Cat.prototype = Animal.prototype;
//再次考虑,还有谁可以访问Animal的原型对象?Animal的实例化对象
//把父类的实例化对象 赋值给 子类的原型对象 此时可以实现原型链继承
Cat.prototype = new Animal();
//如果重新设置了原型对象,那么新的原型对象的constructor就可能出问题
//为了保证程序合理,要把constructor指向修改正确
Cat.prototype.constructor = Cat; //修正constructor
Cat.prototype.do = function () {
console.log("玩红外线");
}
var cat1 = new Cat("white");
console.log(cat1)
console.log(cat1.say)
console.log(cat1.constructor)
console.log(Cat.prototype.constructor)
function Animal(name, age) {
this.name = name;
this.age = age;
}
Animal.prototype.say = function () {
cosnole.log("吼~")
}
var ani = new Animal();
ani.do = function () {
}
function Cat(color, name, age) {
this.color = color;
//构造函数继承
Animal.call(this, name, age)
}
//原型链继承
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.do = function () {
console.log("玩红外线");
}
var cat1 = new Cat("white", "wanan", "2");
console.log(cat1)
console.log(cat1.say)
console.log(cat1.constructor)
console.log(Cat.prototype.constructor)
JS对象多态性是与生俱来的:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果,从而,给不同的对象发送同一个消息时,这些对象根据这个消息分别表示不同的反馈。
定时器真的是定时执行的吗?
定时器变量函数是在分线程执行的吗?
事件循环模型(事件引发机制)
事件轮询机制
先执行同步代码
等待同步代码执行完成后才执行异步代码
事件管理模块
回调队列
function Car(color, speed) {
this.color = color;
this.speed = speed;
return {
name: "lily"
}
}
Car.prototype.run = function () {
console.log("驾~")
}
//封装一个手写new函数,当把构造函数传进去,就会模拟new 把它给实例化了
function writeNew(Car) {
//1
var o = {};
//2 arguments是实参,要的是取掉第一个实参的值
//把arguments转成数组,并从下标为1开始切割 即可得到实例化对象的参数
//result是调用car的返回值
var result = Car.apply(o, Array.prototype.slice.call(arguments, 1));
//3
o.__proto__ = Car.prototype;
//判断Car构造函数的返回值,如果是Object类型 则丢弃o 如果是基本类型 则返回o
var isObj = typeof result === 'object' && result != null;
var isFun = typeof result === 'function';
return (isObj || isFun) ? result : o;
}
var car1 = writeNew(Car, "red", "200");
var car2 = writeNew(Car, "green", "220");
console.log(car1)
console.log(car1 instanceof Car)
console.log(car2 instanceof Car)
console.log(car1.constructor)
console.log(car2.constructor)
console.log(car1.run === car2.run)
//new:1.返回一个对象 2.对象上还有属性和方法
var car3 = new Car("red", "200");
var car4 = writeNew(Car, "green", "220")
console.log(car3, car4)
如果变量声明和函数名相同,如果变量在没有赋值的情况下,不会影响函数执行
var fn1 = new Function("a", "alert(a)");
fn1(1);
function fn2() {
alert(2);
}
var fn3 = function () {
}
fn() //一般调用
new fn() //实例化调用
obj.fn() //对象上下文调用
fn.call() /fn.apply()/fn.bind()(); //call方法。。调用
函数的传参操作其实是值的传递或者是引用值的传递而不是堆内数据的传递,
传参的传递只有值传递 而没有引用传递
o5传递给obj 只是把地址值传递给了obj 而不是把o5所代表的对象传递给obj
function fn() {
}
fn();
fn.study = "js";
回调函数:(一定是一个函数)
常见的场景
- DOM事件
- 计时器
- ajax
- 生命周期函数
div.onclick = function () {
}
[1, 2, 3].forEach(function () {
})
setTimeout(function () {
}, 0)
匿名函数执行 把匿名函数赋值给一个变量,变量调用
IIFE:匿名函数自调用
(匿名函数)()
+匿名函数()
IIFE的作用:
- 构建局部作用域(防止变量污染)
- 模块化
(function () {
})();
! function () {
}();
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i); //5 5 5 5 5
})
}
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i); //5 5 5 5 5
})
})(i)
}
this是函数中的一个变量,他指向的对象是根据函数调用时候决定的
- 首先确实是否是硬绑定,如果 是则this指向call所规定的对象
- 是否是实例化调用,如果是实例化调用,则this指向实例化对象
- 判断函数调用是否是被上下文对象调用的,如果是 则this指向上下文对象(要注意是否存在隐式丢失现象)
- 函数自调用 返回window
function fn1() {
console.log(this);
}
//1.自调用(默认绑定) 自调用的this指向window
fn1();
[1, 2, 3].forEach(function (item, index) {
console.log(this); //window 回调函数是自己调用的,数组调用的是forEach 而不是forEach中的回调
})
setTimeout(function () {
console.log(this); //当事件到了之后调用,回调函数自己调用,指向window
})
// 2.对象调用(隐式绑定)
var obj = {
name: "lily",
fn: fn1 //把fn1变量保存的引用地址 给了fn
}
obj.fn() //此时调用的fn1函数 函数调用有一个上下文对象obj 所以this指向的是obj
document.onclick = fn; //事件函数this指向 绑定事件的元素
//3.隐式丢失
var obj1 = {
name: "lily",
fn: fn1 //把fn1变量保存的引用地址 给了fn
}
var fn2 = obj1.fn; //把obj1的fn属性保存的引用地址给了fn2
fn2(); //window 这个属于默认绑定 自调用 和obj1没有任何关系
//4.实例化调用
var f = new fn1(); //this指向实例化对象 指向f变量保存的对象
//5.显示绑定(硬绑定)call apply bind
fn1.call(window) //window
fn1.call(obj1) //obj1
每一个函数都有这三个方法
这三个方法作用一致,都是改变函数运行时的上下文指向,其实就是改变函数中的this指向
fn.call(fn的this指向,fn函数的参数 参数和参数之间逗号隔开)
fn.apply(fn的this指向,[数组,里边书写的是fn的参数])
fn.bind(fn的this指向,fn函数的参数 参数和参数之间逗号隔开) 和call类似
bind 只是改变了this指向,并且返回了一个新函数的引用,并没有调用函数
被称作显示原型
每一个原型对象都有一个constructor属性,指向当前原型对象的构造函数
构造器
a.constructor 获取的是a的构造函数
'123'.constructor --> f String
[].constructor --> f Array
__proto__
每一个对象都有一个__proto__属性,我们把这个属性称作为隐式原型
在ES6之前,我们是不能操作隐式原型的
每一个函数都有一个显示原型 prototype
每一个对象都有一个隐式原型 __proto__
对象的隐式原型指向对象构造函数的显示原型
实例化的时候已经确定好了整个原型链