JS基础与进阶讲解

JS基础讲解

9.1、JS基础语法

JavaScript 是一种用于在客户端(即用户的浏览器)上运行的编程语言。它是一种脚本语言,可以直接嵌入到 HTML 页面中,并由浏览器解释和执行。

在客户端环境中,JavaScript 可以与用户交互,操作和修改网页的内容、结构和样式。它可以用于处理用户输入、响应事件、进行表单验证、创建动态效果等。JavaScript 还可以通过浏览器提供的 API 访问浏览器功能,例如操作 DOM(文档对象模型)、发送网络请求、处理本地存储等。

需要注意的是,JavaScript 也可以在服务器端运行,例如使用 Node.js 环境。在服务器端,JavaScript 可以用于构建网络应用、处理后端逻辑等。但在这种情况下,它并不是典型的“客户端语言”,而是作为一种通用的脚本语言使用。

from flask import Flask, render_template

app = Flask(__name__, template_folder="templates", static_folder='static')


@app.get("/index")
def index():
    return render_template("index.html")


app.run()

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        h3 {
            color: green;
        }
    </style>

</head>
<body>

<h3>hello Alex</h3>

<script src="/static/a.js"></script>

</body>
</html>

在这里插入图片描述

【1】基本语法

// JS代码导入方式:
// 1 直接编写
   /*
    <script>
        console.log('hello yuan')
    </script>
    */
// 2 导入文件

// <script src="hello.js"></script>


// 单行注释
/*
    这是
    多行
    注释
        */
     
// 语句分隔符 ; 表示一行代码的结束

【2】变量与数据类型

// (1) 声明变量
var x;  // 声明一个变量叫x, x没有值. 此时打印x的内容是undefined(没有被定义)

// 对x进行打印看看效果.
// 在控制台记录xxxx => python中的print()
// 查看打印的内容在F12 -> Console -> 可以看到打印的内容

console.log(x);

// (2) 基本数据类型:number string  bool

var x = 3.1415; // 声明变量x, 并且给x一个初始值. 123
console.log(typeof x); // 可以查看数据类型. 但是只能看基础数据类型.

var x = "hello yuan";
console.log(typeof x); // string 字符串

var x1 = true;
var x2 = false; // 布尔值. python里面是大写的. js里面是小写.

console.log(typeof x1); // boolean 布尔. python简化成bool

// (3) 高级数据类型:  数组[]    对象{}    

var x = [1,2,3]; // 数组. => python的列表.
console.log(typeof x);  // 类型叫object

var x = {name:"yuan",age:22}; // 对象 => python的字典.
console.log(typeof x); // 也是object

// (4) 两个特殊值:   undefined null

var x; // undefined
console.log(typeof x);  // undefined类型... 不占用内存空间的

var x = null; // 空.... 是python的None, 要使用堆内存的....
console.log(typeof x); // object类型

// 一次性可以声明多个变量
var name="yuan", age=20, job="lecturer";
var a, b, c=3, d, e = 5; // 从左到右,声明5个变量, 其中, c=3, e=5

类型转换:

// 在js中. 所有的数据和字符串相加. 结果都是字符串拼接.(重点)
var a = 123;
var b = "我爱你";
console.log(a + b);
console.log(1+1); // 2

var a = "123"; // 字符串
// 字符串转化成整数
// parse 转换
// Int 整数
a = parseInt(a);
console.log(a + 3); // 126 数学上的加法. 证明转化成功了.

var a = 123; // 转化成字符串
var b = a.toString(); // 把数字, 转化成字符串
console.log(b + 3); //"1233"

// 一般的程序员不用toString()的.
var a = 123;
var b = a + ""; // 在逆向的过程中. 这种写法是最多的.
console.log(b+333); // "123333" 字符串


var a = 123;
// 数字的toString()可以指定进制
var b = a.toString(16);
console.log(b);

var m = '7b'; // 眼睛看着是字符串. 但是我们知道它应该是一个十六进制的字符串.
// 字符串, 转化成 整数   parseInt
var n = parseInt(m, 16);
console.log(n)

【3】运算符

// (1) 科学运算符
 + - * / %

// (2)比较运算符
> <   <=  >= === !==

// (3) 赋值运算符 
+= -= *= /=  ++

// 在单独使用a++和++a是没有区别的. 都是让a自增1
var b = a ++;  // 先赋值, 后运算...

console.log(a); // 11
console.log(b); // 10

var b = ++ a; // 先计算, 后赋值
console.log(a);
console.log(b);

// (4)逻辑运算符: Python中的and, or, not
// &&, 并且, 左右两端必须同时为真, 结果才能是真..
// ||, 或者, 左右两端, 有一个是真, 结果就是真
// !, 非/不, 非真即假, 非假既真.

console.log(1 > 2 || 3 < 5);
console.log(1 > 2 && 3 < 5);

console.log(!true);
console.log(!false);

// x() && y()
// 该逻辑叫短路...如果前面的表达式已经得到结果了. 后面就不运行了. 相同的逻辑还有||
// 如果x()返回真. 则运行y()
// 如果x()返回假, 则不运行y(). 直接结束判断了.


// (5)逗号运算符
a = 1,2,3,4
b = (1,"yuan",true)
// (6)三目运算:条件? 成立: 不成立
// 案例1
var a = 100;
var b = 20;
var c = a > b ? a: b;
console.log(c);
// 案例2:终极一练

let a = 10;
let b = 20;
let c = 5;
let d = 17;

let e;
let m;
e = (e = a > 3 ? b : c, m = e < b++ ? c-- : a = 3 > b % d ? 27: 37, m++);
// e = a > 3 ? b : c; // e = 20
// b = 21 , m 和 a都是37
// m = e < b++ ? c-- : a = 3 > b % d ? 27: 37;
// e = m++; // e 37  m 38  a 37

【4】流程控制语句

分支语句

// 分支语句
if(条件){
     // 条件为true时,执行的代码
   }else{
     // 条件为false时,执行的代码
 }  

switch(条件){
    case 结果1:
         // 满足条件执行的结果是结果1时,执行这里的代码..
         break;
    case 结果2:
         // 满足条件执行的结果是结果2时,执行这里的代码..
         break;
    ...
    default:
         // 条件和上述所有结果都不相等时,则执行这里的代码
   }


// (1)双分支
var age = 23
if (age > 18) {
    console.log("播放成人电影!")
} else {
    console.log("播放未成年电影")
}

// (2)多分支
var score = 45;
if (score >= 90) {
    console.log("A");
} else if (score >= 80) {
    console.log("B");
} else if (score >= 70) {
    console.log("C");
} else if (score >= 60) {
    console.log("D");
} else {
    console.log("E");
}

// (3)switch多分支
var week = 3;
switch(week){
    case 1:
        console.log("星期一");
        break;
    case 2:
        console.log("星期二");
        break;
    case 3: // 某一个case匹配成功. 那么后面的case就不判断了, 直接被执行.
        console.log("星期三");
        break;
    case 4:
        console.log("星期四");
        break;
    case 5:
        console.log("星期五");
        break;
    case 6:
        console.log("星期六");
        break;
    case 7:
        console.log("星期天");
        break;
    default:
        console.log("啥也不是!");
        break;
}

循环语句

/*

// 循环语句
while(循环的条件){
      // 循环条件为true的时候,会执行这里的代码
   }
   
// 循环三要素
for(1.声明循环的开始; 2.条件; 4.循环的计数){
   // 3. 循环条件为true的时候,会执行这里的代码
} 
 
 * 1. 首先, 会执行语句1, 通常会在语句1里完成对循环变量的初始化
 * 2. 然后, 判断表达式2计算的结果是否是真, 如果是真, 则执行循环体, 如果是假, 结束循环
 * 3. 执行完循环体后. 执行表达式3,然后回到第二步...
 
 */

// while 循环
var a = 0; // 1. 初始变量
while (a < 100) {  // 2. 条件判断
    console.log("我爱你");  // 业务需求...循环内容...
    a++;  // 3. 步进语句
}


for(var i = 0; i < 10; i++){
    console.log(i);
}

【5】字符串对象

// js的字符串对象
var s = "Hello Yuan      "
console.log(s.toUpperCase())
console.log(s.toLowerCase())
console.log(s.slice(0, 5))
console.log(s.split(" ")) // ["Hello","Yuan"]
console.log(s.length)
console.log(s.trim(" ").length)
console.log(s.trimEnd(" ").length)
console.log(s.indexOf("Yuan"))
console.log(s.replace("Yuan","Rain"))
console.log(s.startsWith("Hel"))

【6】数组

// 注意, 前端js里面的数组. 相当于python的列表, java的List

// 创建数组的方法;
// 1. 直接赋值一个数组
var arr1 = [11,22,33];
console.log(arr1);

// new表示创建对象.  理解成分配内存.
var arr2 = new Array();  // var arr2 = []


// 1. push 在数组的末尾添加数据...
// 2. pop 在数组的末尾删除数据
var arr = [11,22,33,44];
arr.push(55);
var item = arr.pop();
console.log(item);


// 1 unshift 在数组的首位添加数据...  2 shift 删除数组的首位数据

var arr = [11,22,33];
arr.unshift(0); // 在数组的前面插入数据
// console.log(arr);
var item = arr.shift();
console.log(item);
console.log(arr);

var arr = ["a","b","c"];
arr.splice(1,1);
arr.splice(1,0,"b")
arr.splice(1,1,"B")

arr.reverse();

// slice(开始下标,结束下标)  切片,开区间
arr.slice(1,3)

var arr = ["北京","深圳","广州"];
console.log(arr.join(",")); // 把数组转化成字符串

// 循环和遍历
//  key       0         1       2       3       4         5
var arr = ["朱元璋", "朱允炆", "朱棣", "朱高炽", "朱高煦", "朱瞻基"];

// 三要素for循环
for(var i = 0; i < arr.length; i++){
    console.log(arr[i]);
}

// for-in
for(var x in arr){ // 此时拿到的是数组的索引(key)
    console.log(x);
}

// for-of
for (var x of arr){ // 此时拿到的是数组的元素(value)
    console.log(x);
}

// arr中的每一个元素执行里面传递进去的函数
// 回调函数的参数是 元素和索引

console.log(ret);

【7】对象

var obj = {
          "name": "yuan",
          age: 18,
          girl_friend: {
              name: "高圆圆",
              age: 28
          }
      }

// 当他是python的字典, 无缝衔接的. `[]` 也可以翻译成`的`
console.log(obj['girl_friend']['name']);
// 当他是对象... 对象.属性  `.`可以翻译成`的`
console.log(obj.name)
console.log(obj.girl_friend.name)

// 它俩可以混着来.
var w = "girl_friend"
console.log(obj[w].name);
console.log(obj.girl_friend['name']);
// js的对象在使用的时候. 既可以通过`[]`来获取属性的值. 也可以通过`.`来获取属性的值
// 区别: []里面必须放字符串.
//       .后面放的必须是属性名(不可以是字符串)
console.log(obj.girl_friend.name); // 68

// 如果想要对代码进行伪装,混淆. 就不能用`.`

console.log(obj['girl_friend']['name']); // 高圆圆
console.log(obj.girl_friend.name); // 高圆圆


var obj = {};
obj.name = "alvin";  // 对象.属性 = 值. 可以给对象增加新的属性(设置一个新的属性)
obj['age'] = 28;

console.log(obj);

序列化和反序列化:

方法描述
JSON.stringify(obj)把obj对象转换成json格式字符串,会移除对象方法
JSON.parse(str)把符合json语法的字符串转换成js对象

【8】其它常用对象

// Math对象 Date类
var now=new Date();
console.log(now.toLocaleString( ));

// console.log(Math.random());
console.log( Math.round(Math.random() * 10 ));

9.2、函数

【1】函数初识

// 函数声明
function 函数名(形参){
   return // 返回值
}

// 函数调用
函数名(实参)

// 编译运行
// 默认返回undefined
// arguments参数

【2】匿名函数

在 JavaScript 中,匿名函数是一种没有名称的函数定义。匿名函数可以被直接传递给其他函数作为参数,也可以被赋值给变量或对象的属性,以便稍后调用。

// 匿名函数赋值变量
 var foo = function () {
     console.log("这是一个匿名函数!")
 };

// 匿名函数的自执行
(function (x,y) {
     console.log(x+y);
 })(2,3)

// 匿名函数作为一个高阶函数使用
function bar() {

  return function () {
      console.log("inner函数!")
  }
}

bar()()

匿名函数常用于回调函数、事件处理程序、异步操作等场景,它们允许我们在需要时动态定义函数逻辑,并将其作为值传递或存储,而不需要为函数命名。这提供了更灵活和简洁的编程方式。

【3】作用域

作用域(Scope)是指在程序中定义变量的可访问性和可见性的范围。它决定了在代码中的哪些位置可以访问或引用特定变量、函数或对象。

在大多数编程语言中,包括 JavaScript,在不同的作用域中定义的变量具有不同的可见性和生命周期。常见的作用域类型有以下几种:

  1. 全局作用域(Global Scope):全局作用域是在整个程序中都可访问的作用域。在全局作用域中声明的变量可以在程序的任何位置被访问。
  2. 函数作用域(Function Scope):函数作用域是在函数内部定义的作用域。在函数作用域中声明的变量只能在函数内部被访问,它们对于函数外部是不可见的。每当函数被调用时,都会创建一个新的函数作用域。
  3. 块级作用域(Block Scope):块级作用域是在代码块(通常由花括号 {} 包围)内定义的作用域。在块级作用域中声明的变量只能在该块内部被访问,而在块外部是不可见的。在 ES6 中引入的 letconst 关键字可以用来创建块级作用域。
//  首先熟悉下var

var name = "yuan"; // 声明一个全局变量 name并赋值”yuan“
name = "张三";  // 对已经存在的变量name重新赋值 ”张三“
console.log(name);

age = 18   // 之前不存在age变量,这里等同于var age = 19 即声明全局变量age并赋值为18

var  gender = "male"
var  gender = "female" // 原内存释放与新内存开辟,指针指向新开辟的内存
console.log(gender)

作用域案例:

var num = 10; // 在函数外部声明的变量, 全局变量
function func(){
    // num = 20; // 函数内部直接使用变量,则默认调用了全局的变量,
    //var num = 20;                  
    console.log("函数内部num:",num)
}
func();
console.log("全局num:",num);

作用域的正确使用可以避免变量名冲突和数据泄漏,并提供更好的代码封装和隔离性。理解作用域的概念对于编写可维护和可扩展的代码非常重要。

【4】闭包函数

闭包(closures)是 Javascript 语言的一个难点,也是它的特色,很多高级应用都是依靠闭包实现的。闭包与变量的作用域以及变量的生命周期密切相关,本节我们就来简单介绍一下。

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量(外部非全局)的函数。

简单来说就是一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行。这样的一个函数我们称之为闭包函数。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        function getCounter() {

            var count = 0

            function counter() {
                var name = "yuan"
                count++  // count = count+1
                console.log(count)
            }

            // counter()
            // counter()
            // counter()
            return counter
        }

        c1 = getCounter()
        c1()
        c1()
        c2 = getCounter()
        c2()
        c2()

    </script>
    <script>
        var count = 100
    </script>
</head>
<body>

<button onclick="c1()">add1</button>
<button onclick="c2()">add2</button>
</body>
</html>

9.3 、Window对象

window 是客户端浏览器对象模型的基类,window 对象是客户端 JavaScript 的全局对象。一个 window 对象实际上就是一个独立的窗口,对于框架页面来说,浏览器窗口每个框架都包含一个 window 对象。

(1)全局作用域

在客户端浏览器中,window 对象是访问 BOM 的接口,如引用 document 对象的 document 属性,引用自身的 window 和 self 属性等。同时 window 也为客户端 JavaScript 提供全局作用域。

由于 window 是全局对象,因此所有的全局变量都被解析为该对象的属性。

<script>

    var username = "yuanhao";  //全局变量
    function f() {  //全局函数
        console.log(username);
    }
  
    console.log(window.username);  //返回字符串“yuanhao”
    window.f();  //返回字符串“yuanhao”

</script>


(2)使用系统对话框

window 对象定义了 3 个人机交互的方法,主要方便对 JavaScript 代码进行调试。

  • alert():确定提示框。由浏览器向用户弹出提示性信息。该方法包含一个可选的提示信息参数。如果没有指定参数,则弹出一个空的对话框。
  • confirm():选择提示框。。由浏览器向用户弹出提示性信息,弹出的对话框中包含两个按钮,分别表示“确定”和“取消”按钮。如果点击“确定”按钮,则该方法将返回 true;单击“取消”按钮,则返回 false。confirm() 方法也包含一个可选的提示信息参数,如果没有指定参数,则弹出一个空的对话框。
  • prompt():输入提示框。可以接收用户输入的信息,并返回输入的信息。prompt() 方法也包含一个可选的提示信息参数,如果没有指定参数,则弹出一个没有提示信息的输入文本对话框。

(3)访问客户端对象

使用 window 对象可以访问客户端其他对象,这种关系构成浏览器对象模型,window 对象代表根节点,浏览器对象关系的关系如图所示,每个对象说明如下。

  • window:客户端 JavaScript 顶层对象。每当 或 标签出现时,window 对象就会被自动创建。
  • navigator:包含客户端有关浏览器信息。
  • screen:包含客户端屏幕的信息。
  • history:包含浏览器窗口访问过的 URL 信息。
  • location:包含当前网页文档的 URL 信息。
  • document:包含整个 HTML 文档,可被用来访问文档内容及其所有页面元素。

(4)使用定时器

window 对象包含 4 个定时器专用方法,说明如下表所示,使用它们可以实现代码定时执行,或者延迟执行,使用定时器可以设计演示动画。

方法说明
setInterval()按照执行的周期(单位为毫秒)调用函数或计算表达式
setTimeout()在指定的毫秒数后调用函数或计算表达式
clearInterval()取消由 setInterval() 方法生成的定时器
clearTimeout()取消由 setTimeout() 方法生成的定时器

setTimeout() 方法能够在指定的时间段后执行特定代码。用法如下:

var o = setTimeout(code, delay)

参数 code 表示要延迟执行的字符串型代码,将在 Windows 环境中执行,如果包含多个语句,应该使用分号进行分隔。delay 表示延迟时间,以毫秒为单位。

该方法返回值是一个 Timer ID,这个 ID 编号指向延迟执行的代码控制句柄。如果把这个句柄传递给 clearTimeout() 方法,则会取消代码的延迟执行。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<input id="ID1" type="text" >
<button onclick="begin()">开始</button>
<button onclick="end()">停止</button>

<script>
    function showTime(){
           var nowd2=new Date().toLocaleString();
           var temp=document.getElementById("ID1");
           temp.value=nowd2;

    }
    var ID;
    function begin(){
        if (ID==undefined){
             showTime();
             ID=setInterval(showTime,1000);
        }
    }
    function end(){
        clearInterval(ID);
        ID=undefined;
    }

</script>

</body>
</html>

9.4、jQuery

jQuery是一个快速、简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架)。jQuery设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。

jQuery的核心特性可以总结为:具有独特的链式语法和短小清晰的多功能接口;具有高效灵活的css选择器,并且可对CSS选择器进行扩展;拥有便捷的插件扩展机制和丰富的插件。jQuery兼容各种主流浏览器,如IE 6.0+、FF 1.5+、Safari 2.0+、Opera 9.0+等

目前在市场上, 1.x , 2.x, 3.x 功能的完善在1.x, 2.x的时候是属于删除旧代码,去除对于旧的浏览器兼容代码。3.x的时候增加es的新特性以及调整核心代码的结构

根本上jquery就是一个写好的js文件,所以想要使用jQuery的语法必须先引入到本地

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
查找标签
/*
基本选择器 :

$("#id")   
$(".class")  
$("element")  
$(".class,p,div")

后代选择器:
$(".outer div") 

筛选器:
  $().first()       
  $().last()          
  $().eq(2)     
  
导航查找:  

$("div").children(".test")     
$("div").find(".test")  
                               
// 向下查找兄弟标签 
$(".test").next()               

// 查找所有兄弟标签  
$("div").siblings()  
              
// 查找父标签:         
$(".test").parent() 
  
*/
绑定事件
// 绑定事件
$().on("事件名",匿名函数)
// $().click()
操作标签
  • 文本操作
$("选择符").html()     // 读取指定元素的内容,如果$()函数获取了有多个元素,提取第一个元素
$("选择符").html(内容) // 修改内容,如果$()函数获取了多个元素, 则批量修改内容
  • value操作
$().val()
  • 属性操作
//读取属性值
	$("选择符").attr("属性名");   // 获取非表单元素的属性值,只会提取第一个元素的属性值
//操作属性
  $("选择符").attr("属性名","属性值");  // 修改非表单元素的属性值,如果元素有多个,则全部修改
  • css样式操作
// 操作样式
$().css("样式属性","样式值")
  • class 属性操作
$().addClass("class1  class2 ... ...")   // 给获取到的所有元素添加指定class样式
$().removeClass() // 给获取到的所有元素删除指定class样式
$().toggleClass() // 给获取到的所有元素进行判断,如果拥有指定class样式的则删除,如果没有

Ajax请求

Ajax,一般中文称之为:“阿贾克斯”,是英文 “Async Javascript And Xml”的简写,译作:异步js和xml传输数据技术。

ajax的作用: ajax可以让js代替浏览器向后端程序发送**http**请求,与后端通信,在用户不知道的情况下操作数据和信息,从而实现页面局部刷新数据/无刷新更新数据。

所以开发中ajax是很常用的技术,主要用于操作后端提供的数据接口,从而实现网站的前后端分离

ajax技术的原理是实例化js的XMLHttpRequest对象,使用此对象提供的内置方法就可以与后端进行数据通信。

数据接口

数据接口,也叫api接口,表示后端提供操作数据/功能的url地址给客户端使用。

客户端通过发起请求向服务端提供的url地址申请操作数据【操作一般:增删查改】

同时在工作中,大部分数据接口都不是手写,而是通过函数库/框架来生成。

ajax的使用

ajax的使用必须与服务端程序配合使用,但是开发中我们对于ajax请求的数据,不仅仅可以是自己写的服务端代码,也可以是别人写好的数据接口进行调用。

数据接口:

# 天气接口
https://v0.yiketianqi.com/api?unescape=1&version=v91&appid=43656176&appsecret=I42og6Lm&ext=&cityid=&city=

# 音乐接口
https://c.y.qq.com/v8/fcg-bin/fcg_v8_toplist_cp.fcg?g_tk=5381&uin=0&format=json&inCharset=utf-8&outCharset=utf-8%C2%ACice=0&platform=h5&needNewCode=1&tpl=3&page=detail&type=top&topid=36&_=1520777874472%E4%BD%9C%E8%80%85%EF%BC%9Atsia%E9%93%BE%E6%8E%A5%EF%BC%9Ahttps://www.jianshu.com/p/67e4bd47d981

9.5、JS进阶

【1】原型对象(prototype)

案例1:

// 创建方式1
var foo = new Function("console.log('hello world!')")
foo()

// 创建方式2
function Person() {
      console.log("hello person")
}

var p1 = Person()
console.log(p1)

var p2 = new Person()
console.log(p2)
什么是JS原型对象?
  1. prototype(原型对象)就是一个容器. {}
  2. 存放公共的方法给对象使用.
  3. 对象可以直接访问原型对象中的方法和属性.
  4. 原型对象类似Python的类对象(fn.prototype)
原型对象和函数之间的关系.
  • 每个函数都会有一个prototype属性,指向原型对象.
  • 每个原型对象都会有一个constructor属性,指向函数.
  • 总结:每个函数与原型对象之间的关系是互相引用.

img

对象和原型对象和函数之间的关系
  • 函数创建对象var o = new Fn();
  • 对象通过__proto__属性,指向原型对象.
  • 对象可以直接调用原型对象中所有的属性和方法.
  • 对象可以通过原型对象中的constructor属性知道构造函数
  • 总结: 原型对象就是一个容器,帮助函数存放公共方法,以及给函数new出来的对象提供公共方法使用.

img

function Dog(name, age) {
    this.name = name;//面试题:构造函数中的this指向谁? 答:指向new创建的对象
    this.age = age;
    this.sleep = function () {
        console.log("sleeping")
    }

}

// Dog("rain",12) // 普通函数
//构造函数
let alex = new Dog("alex", 36);   // 使用new来创建对象
let eric = new Dog("eric", 35);   // 使用new来创建对象

// (1) 函数对象通过prototype容器设置公共属性和方法
Dog.prototype.eat = function (food) {
    console.log(this.name +"吃"+ food);
}

alex.eat("吃骨头")
eric.eat("吃肉")

// (2) 对象通过`__proto__`属性,指向原型对象
console.log(alex.__proto__)
console.log(alex.__proto__.constructor.name)
console.log(alex.__proto__.constructor)

// 看看能不能理解
console.log(alex.__proto__ === Dog.prototype)
console.log(Dog.prototype)

alex.age = 100
console.log(alex.age) // 先查找自己的空间,找不到,去原型中找
console.log(eric.age)

// 猜一猜1
Dog.prototype.sleep =function (){
    console.log("prototype sleeping")
}

alex.sleep()

// 猜一猜2
let zhangSan = new Dog("张三", 35);
Dog.prototype = {
    fly:function (){
        console.log("flying...")
    }
}
// let zhangSan = new Dog("张三", 35);
// zhangSan.fly()
zhangSan.eat("辣条")

【2】call和apply方法

call,apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例(就是每个方法)都有call,apply属性。既然作为方法的属性,那它们的使用就当然是针对方法的了,这两个方法是容易混淆的,因为它们的作用一样,只是使用方式不同。

foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments) == this.foo(arg1, arg2, arg3);

案例1:

function Person(name, age) {
    this.name = name
    this.age = age
}

Person.prototype.eat = function () {
    console.log(this.name + "正在吃东西")
}

p = new Person("yuan", 22)

p.eat()

案例2:

function Person(name, age) {
    this.name = name
    this.age = age
}
p = new Person("yuan", 22)

function eat () {
    console.log(this.name + "正在吃东西")
}
eat.call(p)

案例3:

function Person(name, age) {
    this.name = name
    this.age = age
}
p = new Person("yuan", 22)

function eat () {
    console.log(this.name + "正在吃东西")
}
eat.call(p)
eat.apply(p)

如果call和apply的第一个参数写的是null,那么this指向的是window对象

案例4:

function Person(name, age) {
    this.name = name
    this.age = age
}

p = new Person("yuan", 22)

function eat(a, b, c) {
    console.log(this.name + "正在吃东西")
    console.log(a, b, c)
}

eat.call(p, "1", "2", "3")
eat.apply(p, ["1", "2", "3"])
// apply传递多个参数的时候第二个参数需要传递一个数组

【3】 eval函数

eval() 函数计算 JavaScript 字符串,并把它作为脚本代码来执行。

如果参数是一个表达式,eval() 函数将执行表达式。如果参数是Javascript语句,eval()将执行 Javascript 语句。

eval(string)
// 
eval('[1,2,3,4,5].map(x=>x*x)')

http://tools.jb51.net/password/evalencode

【4】Hook函数

在 JS 逆向中,我们通常把替换原函数的过程都称为 Hook。

function foo() {
    console.log("foo功能...")
    return 123
}
foo()

var _foo = foo

foo = function () {
    console.log("截断开始...")
    debugger;
    _foo()
    console.log("截断结束...")
}
function foo(a, b, c, d, e, f) {
    console.log("foo功能...")
    console.log(a, b, c, d, e, f)
    return 123
}

var _foo = foo

foo = function () {
    console.log("截断开始...")
    // _foo(arguments)
    _foo.apply(this,arguments)
    console.log("截断结束...")
}

foo(1,2,3,4,5,6,7)
案例1: Hook eval
console.log("程序开始")
// eval("console.log('yuan')")
window["e"+"v"+"a"+"l"]("console.log('eval yuan')")
console.log("程序结束")
var _eval = eval

eval = function (src) {
    console.log("eval截断开始...")
    debugger;
    _eval.apply(this, src)
    console.log("eval截断结束...")
}
案例2: Hook JSON.stringify

JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.stringify() 时,则插入断点:

(function() {
    var stringify = JSON.stringify;
    JSON.stringify = function(params) {
        console.log("Hook JSON.stringif:::", params);
        debugger;
        return stringify(params);
    }
})();
案例3: Hook JSON.parse

JSON.parse() 方法用于将一个 JSON 字符串转换为对象,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.parse() 时,则插入断点:

(function() {
    var parse = JSON.parse;
    JSON.parse = function(params) {
        console.log("Hook JSON.parse::: ", params);
        debugger;
        return parse(params);
    }
})();
案例4: Hook Cookie

一般使用Object.defineProperty()来进行属性操作的hook。那么我们了解一下该方法的使用。

Object.defineProperty(obj, prop, descriptor)

// 参数
obj:对象;
prop:对象的属性名;
descriptor:属性描述符;

一般hook使用的是get和set方法,下边简单演示一下

var people = {
    name: '张三',
};

Object.defineProperty(people, 'age', {
    get: function () {
        console.log('获取值!');
        return count;
    },
    set: function (val) {
        console.log('设置值!');
        count = val + 1;
    },
});

people.age = 18;
console.log(people.age);

通过这样的方法,我们就可以在设置某个值的时候,添加一些代码,比如 debugger;,让其断下,然后利用调用栈进行调试,找到参数加密、或者参数生成的地方,需要注意的是,网站加载时首先要运行我们的 Hook 代码,再运行网站自己的代码,才能够成功断下,这个过程我们可以称之为 Hook 代码的注入。

(function(){
   'use strict'
    var _cookie = "";
    Object.defineProperty(document, 'cookie', {
        set: function(val) {
            console.log(val);
            debugger
            _cookie = val;
            return val;
        },
        get: function() {
            return _cookie;
        },
});
})()

【5】Promise对象

  • Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
  • Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。
  • Promise是一个构造函数,通过new来实例化,主要解决异步编程。
  • 在 ES2015 中,Promise 诞生了。Promise 成功解决了回调函数嵌套调用和错误跟踪、回调函数控制权等问题。
  • 一个Promise对象有三种状态:pending(等待中)、fulfilled(已成功)或rejected(已失败)。当Promise对象处于pending状态时,它表示尚未完成,但可能会在未来某个时间完成。
(1)回调函数

了解promise应该先懂回调,简单理解回调函数能够实现异步编程(可以控制函数调用顺序)。紧接着你应该知道回调地狱,或者函数瀑布,就类似如下的代码:

setTimeout(function () {
    console.log("apple");
    setTimeout(function () {
        console.log("banana");
        setTimeout(function () {
            console.log("cherry");
        }, 1000);
    }, 2000);
}, 3000);
console.log("下一个操作")

Promise 的出现就能够将上面嵌套格式的代码变成了较为有顺序的从上到下风格的代码。

(2)基本语法
 new Promise(function(resolve, reject) { });
  • Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
  • Promise 构造函数只有一个参数,这个参数是一个函数,这个函数在构造之后会直接被异步运行,所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject。异步任务执行成功时调用resolve函数返回结果,反之调用reject
  • Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据。

案例1

 function sayHi() {
            var promise = new Promise(function (resolve, reject) {
                // 模拟Ajax请求
                // var data = "hello world"
                // console.log("请求完成,响应数据为:", data)
                // resolve(data)
                setTimeout(function () {
                    var data = "hello world"
                    resolve(data)
                }, Math.random() * 5000)
            })
            return promise
        }

sayHi().then(item => console.log(item + "!!!")) // 异步操作,不影响下面继续执行
console.log("下一个操作!")

在这里插入图片描述

(3)then函数

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数**。**
Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

.then(function (){}, function (){}); 

如果初始函数里面没有指定resolve或者reject函数,那么 .then 函数是不会被调用的,因为之后状态变成了resolved或者rejected才会调用对应的回调函数。

const promise = new Promise((resolve, reject) => {
    console.log("apple");
    //resolve()
});
promise.then(() => {
    console.log("banana");
});
console.log("cherry");
// 输出 "apple"  "cherry"
// 这里没有输出"banana",因为promise实例的状态没有改变,所以没有调用对应状态的回调函数

放开resolve()函数再试一下。结果先cherry后banana是因为 .then方法是微任务,宏任务执行完毕后开始才开始执行微任务。

把上面的resolve函数换成reject()函数:

 const promise = new Promise((resolve, reject) => {
      console.log("apple");
      reject() 
  });
  promise.then(() => {
      console.log("banana");
  });
 console.log("cherry");

在这里插入图片描述

因为.then方法并没有定义rejected状态相对应的回调函数。因为.then方法的第2个参数才是rejected状态的回调函数。所以改为

const promise = new Promise((resolve, reject) => {
    console.log("apple");
    reject()
});
promise.then(() => {
    console.log("banana success");
},() => {
    console.log("banana reject");
});
console.log("cherry");

如果我们此时再在reject后面加上resolve输出结果和上面一样。这是因为状态只会改变一次,之后不会更改的。如果把最开始的代码resolve放中间会怎样?

const promise = new Promise((resolve, reject) => {
    console.log("apple");
    resolve("apple")  // resolve仅仅设置了状态
    console.log("APPLE");
});
promise.then((res) => {
    console.log(res+" banana");
}, () => {
    console.log("banana");
});
console.log("cherry");

promise执行流程:

1. 构造函数中的输出执行是同步的,输出 apple,执行 resolve 函数,将 Promise 对象状态置为resolved,输出APPLE。
2. 注册这个Promise对象的回调then函数。
3. 宏任务继续,打印cherry,整个脚本执行完,stack 清空。
4. eventloop 检查到 stack为空,再检查 microtask队列中是否有任务,发现了 Promise 对象的 then 回调函数产生的 microtask,推入stack,执行。输出apple banana,eventloop的列队为空,stack为空,脚本执行完毕。
(4)链式应用

使用Promise可以更好地处理异步操作,例如网络请求,文件读取等。它避免了回调地狱(callback hell)的问题,使得代码更加容易理解和维护。

 // 第一层:获取用户信息
function getUserInfo(userId) {
    return new Promise((resolve, reject) => {
        // 模拟异步操作,获取用户信息
        setTimeout(() => {
            const userInfo = {
                id: userId,
                name: "John Doe",
                email: "johndoe@example.com"
            };
            resolve(userInfo);
        }, 1000);
    });
}

// 第二层:获取用户订单列表
function getUserOrders(userId) {
    return new Promise((resolve, reject) => {
        // 模拟异步操作,获取用户订单列表
        setTimeout(() => {
            const orders = [
                {id: 1, product: "Product A"},
                {id: 2, product: "Product B"},
                {id: 3, product: "Product C"}
            ];
            resolve(orders);
        }, 2000);
    });
}

// 第三层:获取订单详情
function getOrderDetails(orderId) {
    return new Promise((resolve, reject) => {
        // 模拟异步操作,获取订单详情
        setTimeout(() => {
            const orderDetails = {
                id: orderId,
                status: "Delivered",
                address: "123 Main St"
            };
            resolve(orderDetails);
        }, 1500);
    });
}

// 应用示例
const userId = 123;

getUserInfo(userId)
    .then(userInfo => {
        console.log("User Info:", userInfo);
        return getUserOrders(userInfo.id);
    })
    .then(orders => {
        console.log("User Orders:", orders);
        const orderId = orders[0].id;
        return getOrderDetails(orderId);
    })
    .then(orderDetails => {
        console.log("Order Details:", orderDetails);
    })
    .catch(error => {
        console.error("Error:", error);
    });

console.log("后续操作!!!")

(5)Promise与Ajax请求

原生Ajax:

 function fetchData(url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onload = function () {
            if (xhr.status === 200) {
                resolve(xhr.responseText);
            } else {
                reject(Error(xhr.statusText));
            }
        };
        xhr.onerror = function () {
            reject(Error('Network Error'));
        };
        xhr.send();
    });
}

// 调用fetchData()函数并处理Promise对象的结果
fetchData('https://v0.yiketianqi.com/api?unescape=1&version=v9&appid=47284135&appsecret=jlmX3A6s').then(function (response) {
    // 如果Promise对象成功解决,执行这里的代码
    var data = JSON.parse(response)
    console.log("data:::", data)

}).catch(function (error) {
    // 如果Promise对象被拒绝,执行这里的代码
    console.log('Error loading data:', error);
});

jQuery版本:

function fetchData(url) {
  return new Promise(function (resolve, reject) {
      $.ajax({
          url: url,
          success: function (res) {
              console.log("res:", res)
              resolve(res)
          }
      })
  });
}

// 调用fetchData()函数并处理Promise对象的结果
url="https://v0.yiketianqi.com/api?unescape=1&version=v9&appid=47284135&appsecret=jlmX3A6s"
fetchData(url).then(function (response) {
    // 如果Promise对象成功解决,执行这里的代码
    const container = $('#container');
    const html = response.data.map(item => `<div><span>${item.day}</span><span>${item.wea}</span></div>`).join('');
    container.append(html);
})

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无敌开心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值