JavaScript进阶
目录
- JavaScript进阶
- 变量类型
- tyoeof:
- ==:
- 使用==||===:
- 内置函数有:
- 创建对象:
- 1、字面量创建
- 2、工厂方法创建对象
- 3、构造函数
- 4、使用原型
- 简单原型
- 原型链
- 继承
- 原型继承:
- 借用构造函数
- 组合继承:借用构造函数+原型继承
- 函数
- 函数的定义方式
- 函数声明与函数表达式
- new Function
- this
- 普通函数调用
- 方法调用
- 构造函数调用
- 作为事件处理函数调用
- 作为定时器的参数
- 函数中的方法
- call的应用
- apply的应用
- bind的应用
- 其他方法与arguments的应用
- 高阶函数
- 函数作为参数与sort的内部实现
- 函数作为返回值(可能发生闭包)
- 闭包
- 案例1:
- setTimeout的原理
- 案例2:
- 思考1:
- 思考2:
- 递归:
- 案列1:
- 案例2:
- 浅拷贝:
- 深拷贝:
变量类型
tyoeof:
typeof undefined //undefined
typeof 'abc' //string
typeof 123 //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function
==:
100='100' //true
0 == '' //true
null == undefined //true
使用==||===:
推荐:出了下面这个其他地方全部用三等
if(obj.a==null){//相当于obj.a===null || obj===undefined这个的简写形式
}
内置函数有:
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
Math
…
创建对象:
- 创建一个新对象
- this指向新对象
- 执行代码,赋值等运算
- 返回this
- 如果构造函数返回一个对象,那么这个对象会取代整个new出来的结果。那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象
1、字面量创建
全为静态成员,适用于tools类等
var MyMath = {
PI: 3.1415926,
max: function () { },
min: function () { }
}
console.log(MyMath.PI);
2、工厂方法创建对象
创建对象为Object的对象,而不是一个hero的对象
// 工厂函数创建对象
function createHero(name, blood, weapon) {
var o = new Object();
o.name = name;
o.blood = blood;
o.weapon = weapon;
o.attack = function () {
console.log(this.weapon+'攻击敌人');
}
return o;
}
var hero = createHero('刘备',100,'剑');
3、构造函数
全为实例成员,创建的对象为hero,但多个对象会存储多个同样的attack方法,占用代码冗余
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
this.attack = function () {
console.log(this.weapon+'攻击敌人');
}
}
var hero =new Hero('刘备',100,'剑');
改1:
解决了存储问题,但污染了全局作用域
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
this.attack = attack;
}
function attack() {
console.log(this.weapon + '攻击敌人');
}
var hero = new Hero('刘备', 100, '剑');
4、使用原型
解决了存储问题,同样不污染全局作用域
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
}
Hero.prototype.attack = function () {
console.log(this.weapon + '攻击敌人');
}
var hero = new Hero('刘备', 100, '剑');
简单原型
在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype
属性,这个属性指向函数的原型对象。原型对象,顾名思义,它就是一个普通对象。
原型链
继承
原型继承:
无法设置构造函数的参数
function Person() {
this.name = 'zs';
this.age = '18';
this.sex = '男'
}
function Student() {
this.score = '99';
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
function Teacher() {
this.salary = 3000;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JBpBys6Y-1570539608298)(C:\Users\10592\AppData\Roaming\Typora\typora-user-images\1564197765564.png)]
借用构造函数
方法怎么继承呢?
// 借用构造函数
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex,score) {
Person.call(this,name,age,sex);
this.score = score;
}
组合继承:借用构造函数+原型继承
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function () {
console.log('大家好!我是' + this.name);
}
function Student(name, age, sex, score) {
Person.call(this, name, age, sex);
this.score = score;
}
// 让字类型继承父类型的方法
Student.prototype = new Person();
Student.prototype.constructor = Student;
Student.prototype.exam = function () {
console.log('考试');
}
function Student(name, age, sex, score) {
Person.call(this, name, age, sex);
this.score = score;
}
Teacher.prototype = new Teacher();
Teacher.prototype.constructor = Teacher;
Teacher.prototype.gaijuan = function () {
console.log('改卷');
}
函数
函数的定义方式
函数声明与函数表达式
// 函数的声明
fn();
function fn() {
console.log('test');
}
//报错
// 函数表达式
fn1();
var fn1= function () {
console.log('test');
}
区别:
- 上面代码由于声明提前可看成:所以
fn1()
处报错
//声明提前
function fn() {
console.log('test');
}
var fn1;
fn();
fn1();
fn1= function () {
console.log('test');
}
- 在老版本的IE中
if
语句中的函数声明会提前 但函数表达式并不会发生这样的情况
// 浏览器问题
// 根据条件声明函数
// 在现代浏览器 不会提升if语句中的函数声明
// 在老版本的IE中 if语句中的函数声明会提前
if(true){
function fn() {
console.log('test--true');
}
}else{
function fn() {
console.log('test--false');
}
}
运行结果:
现代浏览器:test--ture
IE8:test--false
new Function
不推荐使用,因为执行速度慢(先解析字符串,在执行)
认识到函数也是对象
var fn = new Function('var name = "张山";console.log(name);')
fn();
console.dir(fn);
this
函数内部的this,是由函数调用的时候来确定其指向
普通函数调用
this指向window
// 1、普通函数
function fn() {}
fn();
//相当于window.fn();
方法调用
this指向obj,指向调用该方法的对象
// 2、方法调用 this指向obj
var obj={
fn:function () {}
}
obj.fn();
构造函数调用
构造函数内部的this指向由构造函数创建的对象,this指向hero实例
// 3、构造函数调用 构造函数内部的this指向由构造函数创建的对象
function Hero(name, blood, weapon) {
this.name = name;
this.blood = blood;
this.weapon = weapon;
}
Hero.prototype.attack = function () {
console.log(this.weapon + '攻击敌人');
}
var hero = new Hero('刘备', 100, '剑');
hero.attack();
作为事件处理函数调用
作为事件处理函数调用 this指向触发该对象的对象,this指向btn
// 4、作为事件处理函数调用 指向触发该对象的对象
btn.onclick=function(){
console.log(this);
}
作为定时器的参数
作为定时器的参数 this指向window
// 5、作为定时器的参数 this指向window
setInterval(function() {
console.log(this);
}, 1000);
函数中的方法
call的应用
- 使伪数组使用数组的方法
- 使
arr
数组可以使用Object
的toString
而不是自己重写的toString
可以找到数组对象的类型
// 伪数组
var obj={
0: 100,
1: 60,
2: 60,
length: 3
};
Array.prototype.push.call(obj,30);
Array.prototype.splice.call(obj,0,2);
console.dir(obj);
var arr = [5,9];
console.log(arr.toString());
console.log(Object.prototype.toString.call(arr));
最后三句输出:
- 5 ,9
- object Array
apply的应用
可以把数组中的每一项展开
// apply
var arrp = [6,10,5,9,3];
console.log(Math.max.apply(Math,arr)); //输出:10
console.log(null,arr); //输出:6 10 5 9 3
bind的应用
- 改变定时器的this指向
- 改变事件处理函数中的this指向
改变定时器的this指向,让this为function中的this,而function的又是obj调用,所以this指向obj
var obj={
name:'zs',
fun:function () {
setInterval(function() {
console.log(this.name);
}.bind(this), interval); //这里的this是function的this,
}
}
obj.fn();//输出zs
btn.onclick = function () {
}.bind(obj);
其他方法与arguments的应用
当函数参数不固定的时候,在函数内部可以通过arguments获取
//其他方法
function fn(x,y) {
// 函数内部有一个私有变量 arguments(它不是fn.arguments)但跟fn.arguments作用一样(伪数组,获取到的是函数的实参)
console.log(arguments);
console.log(fn.arguments); //伪数组,获取到的是函数的实参,可以根据arguments获取实参
console.log(fn.caller); //函数的调用者,全局调用时为null
console.log(fn.name); //函数的名称,字符串类型
console.log(fn.length); //函数的形参个数
}
function test() {
fn(1,3,6);
}
test();
//当函数参数不固定的时候,在函数内部可以通过arguments获取
function max() {
var max = arguments[0];
for(var i=0;i<arguments.length;i++){
if(max<arguments[i]){
max=arguments[i];
}
}
return max;
}
console.log(max(1,2,8,9,1,7,3));
高阶函数
函数作为参数与sort的内部实现
// 1、函数作为参数
function eat(fn) {
setTimeout(function() {
// 吃晚饭
console.log('吃晚饭');
// 吃晚饭后的事情
fn();
}, 2000);
}
eat(function() {
console.log('去唱歌');
});
var arr=[30,55,14,61];
arr.sort(function(a, b) {
return a - b;
})
console.log(arr);
// arr.mySort
Array.prototype.mySort = function (fn) {
for(var i=0;i<this.length-1;i++){
var isSort = true;
for(var j=0;j<this.length-i-1;j++){
if(fn(this[j],this[j+1])>0){
isSort =false;
var temp = this[j];
this[j] = this[j+1];
this[j+1] = temp;
}
}
if(isSort){
break;
}
}
}
var arr=[30,55,14,61];
arr.mySort(function(a,b) {
return a-b;
});
console.log(arr);
函数作为返回值(可能发生闭包)
- 写一个函数,第一次调用生成1-10随机数,以后的每一次调用都返回第一次的随机值
- 求两个数的和n是变化的
100+n
1000+n
10000+n
第一个参数也会变化但却是规定的 - 两个案例都发生了闭包
//写一个函数,调用生成1-10随机整数数
function getRandom() {
return parseInt(Math.random()*10)+1;
}
console.log(getRandom());
// 写一个函数,第一次调用生成1-10随机数,以后的每一次调用都返回第一次的随机值
function getOnceRandom() {
var random=parseInt(Math.random()*10)+1;
return function() {
return random;
}
}
var fn = getOnceRandom();
console.log(fn());
console.log(fn());
console.log(fn());
// 求两个数的和
// 100+n
// 1000+n
// 10000+n
// 第一个参数也会变化但却是规定的
function getFun(m) {
return function(n) {
return m+n;
}
}
var fn100=getFun(100);
var fn1000=getFun(1000);
console.log(fn100(15)); //115
console.log(fn1000(15)); //1015
闭包
在一个作用域中可以访问另一个作用域的变量或者函数
上面两个案例随机数和求和都发生了闭包
//未发生闭包
function fn1() {
var n=10;
return n;
}
fn1();
// 闭包
// 特点:延展了函数作用域的范围
function fun() {
var n=10;
return function() {
return n;
}
}
var f=fun();
console.log(f());
案例1:
输出点击元素的索引
方式一更好,因为外部的自调用函数的作用域并没有释放性能变低,所以闭包不一定更好
// 案例1:输出点击元素的索引
// 方式1:
var heroes = document.getElementById('heroes');
var list = heroes.children;
for (var i = 0; i < list.length; i++) {
var li = last[i];
li.index = i;
li.onclick = function () {
console.log(this.index);
}
}
// 方式2:使用闭包(第一种更好,因为外部的自调用函数的作用域并没有释放,所以闭包不一定更好)
var heroes = document.getElementById('heroes');
var list = heroes.children;
for (var i = 0; i < list.length; i++) {
var li = last[i];
(function (i) {
li.onclick = function () {
console.log(i);
}
})(i);
}
setTimeout的原理
console.log('start');
setTimeout(function() {
console.log('timeout');
}, 0);
console.log('over');
输出:start over timeout
案例2:
点击按钮改变字体大小
// 案例2:
var btn01 = document.getElementById('btn01');
var btn02 = document.getElementById('btn02');
var btn03 = document.getElementById('btn03');
// btn01.onclick = function () {
// document.body.style.fontSize = '12px';
// }
// btn02.onclick = function () {
// document.body.style.fontSize = '14px';
// }
// btn03.onclick = function () {
// document.body.style.fontSize = '16px';
// }
function makeFun(size) {
return function () {
document.body.style.fontSize = size +'px';
}
}
btn01.onclick = makeFun(12);
btn02.onclick = makeFun(14);
btn03.onclick = makeFun(16);
思考1:
没有发生闭包
输出:The Window
var name = "The Window";
var object={
name:'My Obejct',
getNameFun:function () {
return function () {
return this.name;
}
}
}
console.log(object.getNameFun()()); //The Window
思考2:
发生了闭包
输出:My Obejct
// 思考2:
var name = "The Window";
var object = {
name: 'My Obejct',
getNameFun: function () {
var that =this;
return function () {
return that.name;
}
}
}
console.log(object.getNameFun()()); //My Obejct
递归:
一般都要写一个结束条件,不然会堆栈溢出
案列1:
递归实现1+2+3+4+…+n
// 递归实现1+2+3+4+...+n
function getSum() {
if (n===1) {
return 1;
}
return n+getSum(n-1);
}
案例2:
获取斐波那契数列的第n个数
数列:1、1、2、3、5、8、13、21、34
// 斐波那契数列的第n个数
// 数列:1、1、2、3、5、8、13、21、34
function getFB(n) {
if(n===1||n===2){
return 1;
}
return getFB(n-1)+getFB(n-2);
}
浅拷贝:
改变拷贝前的对象,发现普通属性没有随着拷贝前的对象的改变而变化,但是当属性也是一个对象时,会发生改变
// 对象的浅拷贝
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 5,
yellow: '黄色'
}
}
var obj2 = {}
// 浅拷贝封装
function copyObject(o1, o2) {
for (var key in o1) {
o2[key] = o1[key];
}
}
copyObject(obj1, obj2);
obj1.name = 'xxxx';
obj1.dog.name = '大黄';
console.dir(obj2);
深拷贝:
不管属性是什么类型,改变拷贝前的对象都不会影响拷贝后的对象
// 深拷贝:
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 5,
yellow: '黄色'
}
}
var obj2 = {}
// 深拷贝:
function deepCopy(o1,o2) {
for (var key in o1) {
var item = o1[key];
if(item instanceof Object){
o2[key]={};
deepCopy(item,o2[key]);
}else if (item instanceof Array){
o2[key]=[];
deepCopy(item,o2[key]);
}else{
o2[key] = o1[key];
}
}
}
deepCopy(obj1,obj2);
obj1.name = 'xxxx';
obj1.dog.name = '大黄';
console.dir(obj2);
console.log(obj1);