前端之JavaScript高级2

本文深入探讨JavaScript的高级特性,包括继承的三种方式(构造函数、原型、组合继承),堆栈与堆的概念,以及事件轮询的工作机制。此外,还涉及函数的静态成员、闭包的优缺点以及如何将伪数组对象转化为数组。
摘要由CSDN通过智能技术生成

1. js中的继承

1.1 为什么要学习js中的继承

此时我们希望son对象,也拥有far对象的money, house这些属性,但是我们不想再重新写一遍,怎么办?

var far = {
    money : $1000000000,
    house : ['别墅', '大别墅', '有游泳池的打别墅', '城堡'];
}

var son = {
    
}

1.2 两个对象之间的继承

1.2.1 遍历对象

var far = {
    wife : '小龙女',
    money : 1000000000,
    house : ['别墅', '大别墅', '有游泳池的打别墅', '城堡']
}

var son = {
    wife : '小黄飞',
}

//通过for..in遍历far
for(var key in far){
    son[key] = far[key];
}

console.log(son); // 此时son拥有了money和house .

但是以上代码书写存在一个问题 : 把wife的值也继承下来了.我们希望自己有的,就不继承了.所以我们要改一下代码

for(var key in far){
    //自己没有的才继承
    if(!son.hasOwnProperty(key)){
        son[key] = far[key];
	}
}

这样处理解决刚才的问题,但是我们发现两个对象上面存在一样的代码.这样比较占用内存

1.2.2 通过create方法实现继承

通过ceate方法实现继承,可以避免遍历对象方式,占用内存的问题

create方法语法: Object.create(参考对象) 返回一个新的对象

create方法的作用: 返回的新的对象的__proto__ 指向参考对象

var far = {
    wife : '小龙女',
    money : 1000000000,
    house : ['别墅', '大别墅', '有游泳池的打别墅', '城堡']
}

var son = Object.create(far);
son.wife = '小黄飞';
console.log(son.money); //1000000000
console.log(son.house); //['别墅', '大别墅', '有游泳池的打别墅', '城堡']
console.log(son.wife); // 小黄飞
console.log(son.__proto__ === far); //true

1.3 构造函数的继承

我们工作中要经常创建多个具有相同属性的对象,所以经常要写构造函数.

那么构造函数创建的出来的对象该如何实现继承呢?

1.3.1 借用构造函数(继承私有属性)

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayHello = function(){
        console.log('hello, ' + '我是' + this.name );
    }
}

function Student(name, age, score){
    this.score = score;
    Person.call(this, name, age);//借用构造函数
}

var stu = new Student('zs', 18, 100);
console.log(stu); //{score : 100, name : zs, age : 18}
stu.sayHello(); //hello, 我是zs

但是我们一般不会把方法写在构造函数的函数体内,我们把方法写在函数的原型上,那么这个时候构造借用函数就无法继承方法了,我们想要继承原型上的方法.可以用原型继承。

1.3.2 原型继承(继承公有属性)

用来继承方法

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayHello = function(){
        console.log('hello, ' + '我是' + this.name );
    }
}

function Student(name, age, score){
    this.score = score;
}

Student.prototype = new Person();

var stu = new Student('zs', 18, 100);
console.log(stu); //{score : 100}
stu.sayHello(); //hello, 我是undefined

我们发现,方法继承下来了,但是属性却没有继承下来

1.3.3 组合继承

借用构造函数 + 原型继承

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayHello = function(){
        console.log('hello, ' + '我是' + this.name );
    }
}

function Student(name, age, score){
    this.score = score;
    Person.call(this, name, age);
}

Student.prototype = new Person();

var stu = new Student('zs', 18, 100);
console.log(stu); //{score : 100, name : zs, age : 18}
stu.sayHello(); //hello, 我是zs

2.栈和堆

理解JavaScript中的堆和栈:https://www.cnblogs.com/guchengnan/p/9406607.html

说到堆栈,我们讲的就是内存的使用和分配了,没有寄存器的事,也没有硬盘的事。
各种语言在处理堆栈的原理上都大同小异。堆是动态分配内存,内存大小不一,也不会自动释放。栈是自动分配相对固定大小的内存空间,并由系统自动释放。

javascript的基本类型就5种:Undefined、Null、Boolean、Number和String,它们都是直接按值存储在栈中的,每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说,更加容易管理内存空间。

javascript中其他类型的数据被称为引用类型的数据 : 如对象(Object)、数组(Array)、函数(Function) …,它们是通过拷贝和new出来的,这样的数据存储于堆中。其实,说存储于堆中,也不太准确,因为,引用类型的数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。

说来也是形象,栈,线性结构,后进先出,便于管理。堆,一个混沌,杂乱无章,方便存储和开辟内存空间

3.事件轮询机制

                               æ·±å¥æµåºJavascriptäºä»¶å¾ªç¯æºå¶(ä¸)

 

  v8引擎 : 执行js代码的地方(堆,栈)

  webapi : 帮助js计时

  事件轮询: 会一直观察栈和任务队列,并且会把任务对列中的任务拿到栈中执行

  任务队列: 存储定时器事件到了之后/触发了之后的事件处理函数.

小结

上面的这一个流程解释了当浏览器遇到setTimeout之后究竟是怎么执行的,相类似的还有前面图中提到的另外的API以及另外一些异步的操作。
总结上文说的,主要就是以下几点:

  • 所有的代码都要通过函数调用栈中调用执行。
  • 当遇到前文中提到的APIs的时候,会交给浏览器内核的其他模块进行处理。
  • 任务队列中存放的是回调函数。
  • 等到调用栈中的task执行完之后再回去执行任务队列之中的task。
for (var i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(new Date, i);
    }, 1000);
}
console.log(new Date, i);

 

  1. 首先i=0时,满足条件,执行栈执行循环体里面的代码,发现是setTimeout,将其出栈之后把延时执行的函数交给Timer模块进行处理。

  2. 当i=1,2,3,4时,均满足条件,情况和i=0时相同,因此timer模块里面有5个相同的延时执行的函数。

  3. 当i=5的时候,不满足条件,因此for循环结束,console.log(new Date, i)入栈,此时的i已经变成了5。因此输出5。

  4. 此时1s已经过去,timer模块将5个回调函数按照注册的顺序返回给任务队列。

  5. 执行引擎去执行任务队列中的函数,5个function依次入栈执行之后再出栈,此时的i已经变成了5。因此几乎同时输出5个5。

  6. 因此等待的1s的时间其实只有输出第一个5之后需要等待1s,这1s的时间是timer模块需要等到的规定的1s时间之后才将回调函数交给任务队列。等执行栈执行完毕之后再去执行任务对列中的5个回调函数。这期间是不需要等待1s的。因此输出的状态就是:5 -> 5,5,5,5,5,即第1个 5 直接输出,1s之后,输出 5个5;

4. 函数的静态成员和实例成员

成员: 泛指属性和方法

静态成员: 函数也是对象, 函数自己的属性

实例成员: 特指构造函数时,写在构造函数体内,通过this.xxx.给实例添加的属性

function Person(){
	this.name = name; //实例成员
    this.age = age; // 实例成员
}

Person.type = '呵呵'; //静态成员

5.Function的原型图

Functon 自己创造了自己

Object是Function的实例

所有的函数,底层都是new Function()

<script>
//  function fn(x, y){
//    console.log(x+y);
//  } // var fn = new Function();
//  var fn = function (){
//
//  } // var fn = new Function();
//  var fn = new Function('x','y','console.log(x+y)');
//  fn(3,5);

  function fn(){
    
  }
  
 console.log(fn.__proto__);
 console.log(fn.__proto__.__proto__);
 console.log(Function.__proto__.constructor);
 console.log(Object.__proto__.constructor);
</script>

6. 闭包

概念: 函数和函数作用域的结合

通俗理解: 内部函数使用外部函数的变量, 整个外部函数形成了一个闭包

6.1闭包的作用:

  1. 私有化数据

  2. 数据保持

function main(){
    var money = 10000;  //放到局部作用中,防止全局变量污染(私有化数据)

    return {
        queryMoney : function(){
            return money;
        },
        payMoney : function(num){
            money -= num;
        },
        addMoney : function(num){
            money += num;
        }
    }
}

var moneyManger = main();  // 通过moneyManger 可以获取到局部的变量money

6.2闭包的缺点:

由于内部的函数使用了外部函数的变量,导致外部这个函数无法被回收掉.如果代码中大量的存在闭包,可能会导致内存泄露 (不要刻意使用闭包);

6.3闭包案例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<ul>
  <li>第1个li</li>
  <li>第2个li</li>
  <li>第3个li</li>
  <li>第4个li</li>
  <li>第5个li</li>
  <li>第6个li</li>
</ul>
<script>
  
  //不能使用this, 利用闭包,打印每一个li的文本
  var lis = document.getElementsByTagName('li');

//  for(var i = 0; i < lis.length; i++) {
//      lis[i].onclick = function(){
      console.log(this.innerText);
//        console.log(lis[i].innerText);
//      }
//  }
  for(var i = 0; i < lis.length; i++) {
    (function(i){
      lis[i].onclick = function(){
//      console.log(this.innerText);
        console.log(lis[i].innerText);
      }
    })(i);
  }
</script>
</body>
</html>

7. 面向对象编程

// 面向过程的思想: 一步一步的具体去做这个事情

// 面向对象的思想: 需要哪个对象,调用这个对象的某个方法,帮助我们实现具体的需求'

// 面向对象是对面向过程的封装

// 面向对象编程的好处: 1. 维护起来更方便 2.团队协作更加的便捷

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script>
//  面向过程的思想: 一步一步的具体去做这个事情
//  面向对象的思想: 需要哪个对象,调用这个对象的某个方法,帮助我们实现具体的需求'
//    面向对象是对面向过程的封装
//    面向对象编程的好处: 1. 维护起来更方便 2.团队协作更加的便捷
  
//  面向过程:
//  function max(x,y){
//    if(x > y){
//      return x;
//    }else{
//      return y;
//    }
//  }
  
//    function max(){
      arguments 是函数中的一个对象,而 arguments是一个伪数组,里面存储了所有传进来的实参
      console.log(arguments);
//      var max = 0; //用于和数组每一项做比较,如果数组中的值更大,则把数组中的值赋值给max
//      for(var i = 0; i < arguments.length; i++) {
//        if(arguments[i]>max){
//          max = arguments[i];
//        }
//      }
//      return max;
//    }
//
//console.log(max(1, 2, 3, 4, 4, 5, 65, 6));;
  
//  面向对象: 找对象,调用对象的方法
console.log(Math.max(3, 6, 9, 12, 89));

</script>
</body>
</html>

8. 贪吃蛇案例

9.JavaScript中的伪数组对象转化成数组的方法

转自: https://blog.csdn.net/m0_38102188/article/details/81327465

首先什么是伪数组对象呢?
字面意思理解来说,伪数组对象就是长得像数组的对象,拥有0,1,2,3…以及length等key,但是在原型链中没有Array.prototype这一环,所以他没有操作数组的方法push,pop,shift,unshift等。javaScript中常见的伪数组对象有以nodeList为原型的DOM对象,函数的参数arguments对象等。
那么有没有方法将这些伪数组对象转换成数组,从而直接使用push,pop等方法来对其进行操作呢?
答案是肯定的。

var divTags = document.querySelectorAll('div')

ES5提供了一种方法:

var divTagsArray = Array.prototype.slice.call(divTags)
console.log('push' in divTagsArray)     //true

ES6提供了两种方法:

let divTagsArray1 = Array.from(divTags)
console.log('push' in divTagsArray1)  //true

这里的divTags是部署 Iterator 接口的数据类型,所以也可以用以下方式实现转换:

let divTagsArray2 = [...divTags]
console.log('push' in divTagsArray2)  //true

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值