阅读视图

发现新文章,点击刷新页面。

JavaScript小技能:变量

本文正在参加「金石计划」

引言

如果我们没有变量,我们就不得不写大量的代码去枚举和检查输入的名字,然后去显示它们,这样做显然是低效率和不可行的 。

一个变量,就是一个用于存放数值的容器。

变量不是数值本身,它们仅仅是一个用于存储数值的容器。你可以把变量想象成一个个用来装东西的纸箱子。

I 变量的声明

变量是存储值的容器,在 JavaScript 中声明一个新变量的方法是使用关键字letconstvar,let 和 const 关键字允许你创建块作用域的变量。JavaScript 中可以定义重名变量,如果后面定义的变量没有初始化则会按照前面定义的输出 。

如果声明了一个变量却没有对其赋值,那么这个变量的类型就是 undefined。

1.1 let

//let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
let a;
let name = 'Simon';

let x,y,z=3;//只有最后一个变量z 被赋值了3

//给多个变量赋值
//Longhand
let a, b, c;
a = 5;
b = 8;
c = 12;
//Shorthand 简写
let [a, b, c] = [5, 8, 12];

// myLetVariable 在这里 *不能* 被引用

for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
  // myLetVariable 只能在这里引用
}

// myLetVariable 在这里 *不能* 被引用

1.2 const

const 声明一个不可变的常量,这个常量在定义域内总是可见的。

const Pi = 3.14; // 设置 Pi 的值

1.3 var

var 变量名称 使用 var 声明的变量在它所声明的整个函数都是可见的。

var a;
var name = "simon";
// myVarVariable 在这里 *能* 被引用

for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
  // myVarVariable 整个函数中都能被引用
}

// myVarVariable 在这里 *能* 被引用

//JavaScript 与其他语言的(如 Java)的重要区别是在 JavaScript 中语句块(blocks)是没有作用域的,只有函数有作用域。因此如果在一个复合语句中(如 if 控制结构中)使用 var 声明一个变量,那么它的作用域是整个函数(复合语句在函数中)。 

//但是从 ECMAScript Edition 6 开始将有所不同的, let 和 const 关键字允许你创建块作用域的变量。

II 变量类型

可以为变量设置不同的数据类型。

2.1 数据基本类型

  1. number (数字类型), 采用“遵循 IEEE 754 标准的双精度 64 位格式("double-precision 64-bit format IEEE 754 values")表示数字。在 JavaScript(除了BigInt)当中,并不存在整数/整型 (Integer)。可以使用内置函数 parseInt() 将字符串转换为整型,该函数的第二个可选参数表示字符串所表示数字的基(进制):
parseInt("123", 10); // 123
parseInt("010", 10); // 10
parseInt("11", 2); // 3,把一个二进制数字字符串转换成整数值
//parseFloat() 只应用于解析十进制数字
//一元运算符 + 也可以把数字字符串转换成数值:
+ "42";   // 42
+ "010";  // 10
+ "0x10"; // 16
//parseInt() 和 parseFloat() 函数会尝试逐个解析字符串中的字符,直到遇上一个无法被解析成数字的字符,然后返回该字符前所有数字字符组成的数字。
//但是运算符 "+"对字符串的转换方式与之不同, 只要字符串含有无法被解析成数字的字符,该字符串就将被转换成 NaN。
  1. boolean 布尔类型 ,分别是 true 和 false(两者都是关键字)。
//将变量转换成布尔类型
//1. false、0、空字符串("")、NaN、null 和 undefined 被转换为 false
//2. 除了以上情况,其他值被转换为 true。
//可以使用 Boolean() 函数进行显式转换:
Boolean(''); // false
Boolean(234); // true
//JavaScript 会在需要一个布尔变量时隐式完成这个转换操作,比如在 if 条件语句中。
  1. symbol (符号)(ES2015 新增)

ES2015 新增

从 Symbol() 返回的 symbol 值都是唯一的,能作为对象属性的标识符;

developer.mozilla.org/zh-CN/docs/…

2.2 Object (对象类型)

  1. Function (函数),特殊的对象,函数也可以被保存在变量中,并且像其他对象一样被传递。
  2. Array ( 数组)类型
  3. Date (日期)
   var d = new Date();

//1)    获得当前年份    d.getYear()
//2)    获得年份的全称    d.getFullYear()
//3)    获得月份    d.getMonth()
//4)    获得日期    d.getDate()
//5)    获得星期    d.getDay()
//6)    获得时间    d.getHours()
//7)    获得分钟    d.getMinutes()
//8)    获得秒钟    d.getSeconds()

  1. Math 内置对象 Math(数学对象),用以处理更多的高级数学函数和常数。
Math.sin(3.5);
var circumference = 2 * Math.PI * r;     
//1)       ceil()    向上取整
//2)       floor()   向下取整
//3)       round()   四舍五入

  1. RegExp(正则表达式),特殊的对象。
  2. string (字符串类型),JavaScript 中的字符串是一串Unicode 字符序列。它们是一串 UTF-16 编码单元的序列,每一个编码单元由一个 16 位二进制数表示。每一个 Unicode 字符由一个或两个编码单元来表示。
 var str = new String("abc");
//通过访问字符串的 length(编码单元的个数)属性,可以得到它的长度。
"hello".length; // 5 
//字符串也有 methods(方法)能让你操作字符串和获取字符串的信息。
"hello".charAt(0); // "h"  charAt(下标) 
"hello, world".replace("world", "mars"); // "hello, mars"
"hello".toUpperCase(); // "HELLO"
//          indexOf()
//          substring()
//          concat()
//          split()

2.3 特殊类型

  1. undefined (未定义)类型, 一个未被赋值的变量就是 undefined 类型,undefined 实际上是一个不允许修改的常量。
  2. null ( non-value)空类型 , 只有显示声明null才能使用
  3. NaN : (Not a Number 的缩写),如果给定的字符串不存在数值形式,函数会返回一个特殊的值 NaN。
parseInt("hello", 10); // NaN
"test"/123
//把 NaN 作为参数进行任何数学运算,结果也会是 NaN:
NaN + 5; //NaN
//使用内置函数 isNaN() 来判断一个变量是否为 NaN:
isNaN(NaN); // true
  1. 内置的 Error(错误)类型

特殊值:Infinity(正无穷)和 -Infinity(负无穷)

//使用内置函数 isFinite() 来判断一个变量是否是一个有穷数, 如果类型为Infinity, -Infinity 或 NaN 则返回 false:
isFinite(1/0); // false
isFinite(Infinity); // false
isFinite(-Infinity); // false
isFinite(NaN); // false

isFinite(0); // true


III 详解对象类型

JavaScript 中的一切(除了核心类型,core object)都是对象,JavaScript 中的对象,可以简单理解成“名称 - 值”对(而不是键值对)。 “名称”部分是一个 JavaScript 字符串,“值”部分可以是任何 JavaScript 的数据类型——包括对象。

3.1 Function

函数用来封装可复用的功能,函数通常包括参数,参数中保存着一些必要的数据。它们位于括号内部,多个参数之间用逗号分开。document.querySelector 和 alert 是浏览器内置的函数,随时可用。

let myVariable = document.querySelector('h1');
alert('hello!');

定义和声明函数

  function   函数名称(参数列表){
      
    函数体
     }
 //一个 JavaScript 函数可以包含 0 个或多个已命名的变量。
 var fun = new Function("a","b"," return a+b;");//return 语句返回一个值并结束函数
 //JavaScript 允许你创建匿名函数
 var fun = function (a,b){
      return a+b;
           }
 // 注意:Function 类型没有重载,并且 fun 和fun(1,2)结果完全不同;

匿名函数: 因为它没有名字,匿名函数还有另一种我们称之为箭头函数的写法,箭头函数使用() =>代替 function ()

//匿名函数
document.querySelector('html').onclick = function() {
    alert('别戳我,我怕疼。');
}

//箭头函数
document.querySelector('html').addEventListener('click', () => {
  alert('别戳我,我怕疼。');
});

//Longhand
function add(num1, num2) {
   return num1 + num2;
}
//Shorthand
const add = (num1, num2) => num1 + num2;

函数体中有一个名为 arguments 的内部对象,类似于数组的对象,包括了所有被传入的参数。

function avg() {//可以接收任意个数的参数
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
avg(2, 3, 4, 5); // 3.5

使用剩余参数...args 来替换 arguments 的使用,function avg(firstValue, ...args) 会把传入函数的第一个值存入 firstValue,其他的参数存入 args。

function avg(...args) {//所有被传入该函数的参数都被变量 args 所持有。
  var sum = 0;
  for (let value of args) {//将 for 循环替换为 for...of 循环来参数args
    sum += value;
  }
  return sum / args.length;
}

avg(2, 3, 4, 5); // 3.5

JavaScript 允许你通过任意函数对象apply() 方法来传递给它一个数组作为参数列表。

函数也是对象

apply(thisArg, argsArray)
//1. thisArg在 func 函数运行时使用的 this 值。请注意,this 可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
//2. 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或  undefined,则表示不需要传入任何参数。从 ECMAScript 5 开始可以使用类数组对象。

//传给 apply() 的第二个参数是一个数组,它将被当作 avg() 的参数列表使用
avg.apply(null, [2, 3, 4, 5]); // 3.5

const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);

JavaScript 允许以递归方式调用函数,递归在处理树形结构(比如浏览器 DOM)时非常有用。

var charsInBody = (function countChars(elm) {
    if (elm.nodeType == 3) { // 文本节点
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += countChars(child);
    }
    return count;
})(document.body);//命名立即调用的函数表达式(IIFE——Immediately Invoked Function Expression)

3.2 Object

对象是存储在单个分组中的相关功能的集合。

创建一个空对象

var obj = new Object();// js方式创建一个对象
var obj = {};//对象字面量(object literal)

JSON 格式创建一个对象

   var oo = {
        name:"hello",
        age:123,
        getName:function(){
       return oo.name;
            }
       
    }
   alert(oo.getName());

js方式创建一个对象

   var obj = new Object();
   obj.name = "hello";//对象的属性可以通过链式(chain)表示方法访问
// 括号表示法 (bracket notation)
   obj['name'] = 'Simon';
   obj.for = 'Simon'; // 语法错误,因为 for 是一个预留关键字。从 ECMAScript 5 开始,预留关键字可以作为对象的属性名
   obj["for"] = 'Simon'; // 工作正常
   obj.age = 123;
   obj.getName = function (){
       return obj.name;
            }
   alert(obj.name);
   alert(obj.getName());

3.3 Array 数组

Java中的数组,固定长度,存储同一类型数据,连续内存空间。 js中的数组可变长,存储数据不固定,存储空间不一定连续。

JavaScript 中的数组是一种特殊的对象,与普通对象类似以数字为属性名,但只能通过[] 来访问

   var arr = new Array();
    arr[0]  = 123;
    arr[1] = "hello";
    arr[5] = "aaaa";

通过数组字面量(array literal)创建数组

var arr2 = [1,2,3,"hello",,,"aa",];
alert(arr2.length);// IE中长度为8,火狐7

使用数组解构赋值来交换两个变量

//为了交换两个变量,通常使用第三个变量。
let x = 'Hello', y = 55;
//Longhand
const temp = x;
x = y;
y = temp;
//Shorthand
[x, y] = [y, x];

Array.length 并不总是等于数组中元素的个数

var a = ["dog", "cat", "hen"];
a[100] = "fox";
a.length; // 101

a.push(item);//在数组后追加元素


ES2015 引入了更加简洁的for...of循环,可以用它来遍历可迭代对象,例如数组:

for (const currentValue of a) {
  // Do something with currentValue
}
//不推荐使用 for...in 循环历数组,因为它遍历的是数组的索引。如果直接向 Array.prototype 添加了新的属性,使用这样的循环这些属性也同样会被遍历。
for (var i in a) {
  // 操作 a[i]
}

ECMAScript 5 增加了另一个遍历数组的方法,forEach()

["dog", "cat", "hen"].forEach(function(currentValue, index, array) {
  // 操作 currentValue 或者 array[index]
});

数组的属性 :length, 获得数组长度。

数组的API:

方法名称 描述
a.toString() 返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。
a.toLocaleString() 根据宿主环境的区域设置,返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。
a.concat(item1[, item2[, ...[, itemN]]]) 返回一个数组,这个数组包含原先 a 和 item1、item2、……、itemN 中的所有元素。 var arr1 = arr.concat(arr2);//将两个数组进行拼接操作,返回的是拼接好的数组
a.join(sep) 返回一个包含数组中所有元素的字符串,每个元素通过指定的 sep 分隔。 arr2.join("-");
a.pop() 删除并返回数组中的最后一个元素。
a.push(item1, ..., itemN) 将 item1、item2、……、itemN 追加至数组 a。
a.reverse() 数组逆序(会更改原数组 a)。
a.shift() 删除并返回数组中第一个元素。
a.slice(start, end) 返回子数组,以 a[start] 开头,以 a[end] 前一个元素结尾。
a.sort([cmpfn]) 依据可选的比较函数 cmpfn 进行排序,如果未指定比较函数,则按字符顺序比较进行简单排序,默认只能排序0-9 ; 如果排序数字复杂,则必须对sort方法进行修改: arr.sort(function(a,b){return a-b;});
a.splice(start, delcount[, item1[, ...[, itemN]]]) 从 start 开始,删除 delcount 个元素,然后插入所有的 item。splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
a.unshift(item1[, item2[, ...[, itemN]]]) 将 item 插入数组头部,返回数组新长度
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "June"]

months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "May"]

developer.mozilla.org/zh-CN/docs/…

see also

公众号:iOS逆向

JavaScript小技能:运算符:blog.csdn.net/z929118967/…

iOS小技能:链式编程在iOS开发中的应用blog.csdn.net/z929118967/…

iOS小技能:1、yalu102 激活了之后,无法连接ssh的解决方案 2、Reveal的基本使用3、Passionfruit 的实现原理

本文正在参加「金石计划」

前言

  1. 无法连接ssh的解决方案
  2. Reveal的安装、配置及使用
  3. Passionfruit 的实现原理
  4. Mac上Nodejs环境搭建

I 使用yalu102 激活了之后,无法连接ssh的解决方案

yalu102此次越狱工具默认安装了 SSH.采用了 Dropbear 取代 Openssh。#/usr/local/bin/dropbear

-部署安装使用yalu102时,修改dropbear.plist的信息: ProgramArguments的127.0.0.1:22 直接改为22。

如果部署完成,直接修改沙盒的信息的话,记得重启设备。

1.1 获取直接修改对应的配置信息

  • ps -e |grep yalu*
iPhone:~ root# ps -e |grep yalu*
 1174 ??         0:00.80 /var/containers/Bundle/Application/B831448D-BCD0-4F29-BDA6-9FC03903D30C/yalu102.app/yalu102
  • plutil plist 内容
iPhone:/var/containers/Bundle/Application/B831448D-BCD0-4F29-BDA6-9FC03903D30C/yalu102.app root# plutil dropbear.plist
{
   KeepAlive = 1;
   Label = ShaiHulud;
   Program = "/usr/local/bin/dropbear";
   ProgramArguments =     (
       "/usr/local/bin/dropbear",
       "-F",
       "-R",
       "-p",
       22
   );
   RunAtLoad = 1;
}
  • dropbear 参数
 iPhone:/var/containers/Bundle/Application/B831448D-BCD0-4F29-BDA6-9FC03903D30C/yalu102.app root# ps -e |grep dropbear
  228 ??         0:00.05 /usr/local/bin/dropbear -F -R -p 22

1.2 利用wget 安装scp( 解决:sh: scp: command not found)

#两台服务器都要安装scp才能传文件
#wget + 空格 + 要下载文件的url路径
# cydia里面安装wget
#  安装scp,默认安装在当前目录
wget mila432.com/scp
ldid -S scp
# chmod +x scp
chmod 777 scp
mv scp /usr/bin/scp
# 或者使用curl: curl -O mila432.com/scp ./scp
  • dyld: Library not loaded: /usr/lib/libssl.0.9.8.dylib 重新安装openssl
  • 浏览器下载scp
 find . -name "scp" 

II Reveal的安装、配置及使用

目前自己经常使用的是AFlexLoader

2.1. Reveal Loader安装


首先我们打款越狱设备的Cydia,然后在搜索中输入Reveal Loader,并且进行安装即可,下方是安装后的效果。这一步比较简单,安装后重启SpringBoard即可。

iPhone:~ root# cd /Library/RHRevealLoader
-sh: cd: /Library/RHRevealLoader: No such file or directory

进行第二步骤,导入libReveal.dylib

2.2.导入libReveal.dylib


Mac上的Reveal自带了两个库,一个是libReveal.dylib,一个是Reveal.framework。 在未越狱的设备上使用的是后者,本文使用的是前者。

这两个文件位于Reveal中的iOS Library中。Reveal菜单->Help->Show Reveal Library in Finder ->iOS Library。通过上述目录就可以找到我们需要的文件。

 /Users/devzkn/Downloads/kevin-software/ios-Reverse_Engineering/Reveal/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib

mkdir

devzkndeMacBook-Pro:python-client devzkn$ ssh iphone
iPhone:~ root# mkdir /Library/RHRevealLoader
iPhone:~ root# cd /Library/RHRevealLoader
iPhone:/Library/RHRevealLoader root

scp

devzkndeMacBook-Pro:python-client devzkn$ scp  /Users/devzkn/Downloads/kevin-software/ios-Reverse_Engineering/Reveal/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib iphone:/Library/RHRevealLoader/

iPhone:~ root# ls -l /Library/RHRevealLoader/libReveal.dylib
-rwxr-xr-x 1 root admin 3850232 Oct 19 16:17 /Library/RHRevealLoader/libReveal.dylib

2.3、Reveal的使用


  • 选择Reveal的App

在设置中找到Reveal的配置项,在该配置项中我们可以去选择要Reveal的App, 当然对于越狱手机,手机上安装的所有App都可以Reveal。当然也包括从AppStore下载的,也包括iOS系统自带的

  • 查看app的UI层级

Mac上Reveal查看设备上App的UI层级时是不需要使用USB进行连接的,但要保证你的iOS设备与你的Mac在同一个局域网内

  • 注意事项

记得打开对应的app,保证你查看的app 处于运行状态

III Passionfruit 的实现原理

  • 我在使用Passionfruit 的时候,安装步骤碰到的问题是fatal error: 'frida-core.h' file not found,具体的请看Q&A。

  • 安全审计的工具 我觉得iNalyzer 已经过时了,推荐这款Passionfruit;

  • Passionfruit 通过frida注入代码到目标应用实现了个“动态分析iOS应用”的图形界面。

3.1 实现原理

Passionfruit 通过 frida 注入代码到目标应用实现功能,再通过 node.js 服务端消息代理与浏览器通信,用户通过访问网页即可对 App 实现常规的检测任务。 在这里插入图片描述

3.2 安装

3.2.1 安装前准备

  • brew install libimobiledevice
devzkndeMacBook-Pro:passionfruit devzkn$ brew install libimobiledevice

  • brew install yarn
devzkndeMacBook-Pro:passionfruit devzkn$  brew install yarn

  • install npm
brew install npm

3.2.2 运行 npm install 根据 package.json 文件安装依赖。

  • save frida then $ npm install
devzkndeMacBook-Pro:passionfruit devzkn$ npm install
> Passionfruit@0.0.3 postinstall /Users/devzkn/code/demo/passionfruit
> cd gui && (yarn || npm install)
yarn install v1.3.2
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
✨  Done in 231.08s.
up to date in 235.627s

  • npm run build
devzkndeMacBook-Pro:passionfruit devzkn$ npm run build

> Passionfruit@0.0.3 build /Users/devzkn/code/demo/passionfruit
> frida-compile agent -o _agent.js && cd gui && (yarn run build || npm run build)

yarn run v1.3.2

  • npm start
devzkndeMacBook-Pro:passionfruit devzkn$ npm start

> Passionfruit@0.0.3 start /Users/devzkn/code/demo/passionfruit
> cross-env NODE_ENV=production node .

listening on http://localhost:31337

3.3 Q&A

  • prebuild-install http 404 https://github.com/frida/frida/releases/download/10.6.13/frida-v10.6.13-node-v59-darwin-x64.tar.gz
devzkndeMacBook-Pro:passionfruit devzkn$ npm install --save frida@latest

> frida@10.6.28 install /Users/devzkn/code/demo/passionfruit/node_modules/frida
> prebuild-install || node-gyp rebuild

prebuild-install info begin Prebuild-install version 2.3.0
prebuild-install info looking for local prebuild @ prebuilds/frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info looking for cached prebuild @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install http request GET https://github.com/frida/frida/releases/download/10.6.28/frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install http 200 https://github.com/frida/frida/releases/download/10.6.28/frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info downloading to @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz.47369-6d6afc3ef5581.tmp
prebuild-install info renaming to @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info unpacking @ /Users/devzkn/.npm/_prebuilds/https-github.com-frida-frida-releases-download-10.6.28-frida-v10.6.28-node-v59-darwin-x64.tar.gz
prebuild-install info unpack resolved to /Users/devzkn/code/demo/passionfruit/node_modules/frida/build/Release/frida_binding.node
prebuild-install info unpack required /Users/devzkn/code/demo/passionfruit/node_modules/frida/build/Release/frida_binding.node successfully
prebuild-install info install Successfully installed prebuilt binary!
+ frida@10.6.28
added 32 packages in 18.574s

  • Unable to launch iOS app: timeout

启动应用程序失败之后,装置就重启了。这个问题 有点类似Failed to spawn: unable to launch iOS app: timeout

临时解决方式: 手动启动app ,还是可以正常分析的

更多具体的分析请看这里: frida Failed to spawn 解决方案

IV Mac上Nodejs环境搭建

  1. 安装包安装:nodejs下载网址 http://nodejs.cn/download/
  2. 二进制安装:
brew install nodejs

brew install npm

see also

修改汇编的方式: 选中行,选择菜单栏的Modify > Assemble Instruction...,将jne修改成je,然后点击Assemble and Go Next。

公众号:iOS逆向

JavaScript预备知识

本文正在参加「金石计划」

前言

一个有具体功能的完整网页,一般由3部分组成:

  1. HTML(内容和结构): HyperText Markup Language,超文本标记语言。用来结构化网页内容并赋予内容含义,例如定义段落、标题和数据表,或在页面中嵌入图片和视频。

  2. css(样式): Cascading Style Sheets层叠样式表是一种样式规则语言,允许我们精确地设计HTML的样式,例如设置背景颜色和字体,在多个列中布局内容。

  3. JavaScript(交互效果) :JavaScript 是一种符合ECMAScript规范的脚本编程语言,可以用来创建动态更新的内容,控制多媒体,制作图像动画。

脚本语言是为了缩短传统编程语言从编写-编译-运行这个过程而开发的一种简单类型语言。

I 预备知识

1.1 术语:解释(interpret)和 编译 (compile)

编译原理 : https://blog.csdn.net/z929118967/article/details/123778003 在解释型语言中,代码自上而下运行,且实时返回运行结果。

js代码由浏览器执行前,不需要将其转化为其他形式,代码将直接以文本格式(text form)被接收和处理。

编译型语言需要先将代码转化(编译)成另一种形式才能运行。

C++,Objective C都是编译语言,必须先通过编译器生成机器码,然后才能由计算机运行。

Objective-C与swift都采用Clang作为编译器前端,编译器前端主要进行语法分析,语义分析,生成中间代码,在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。

在这里插入图片描述 编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化,根据不同的系统架构生成不同的机器码。

在这里插入图片描述

1.2 语言特点

  1. JavaScript 是轻量级解释型语言。

浏览器接受到 JavaScript 代码,并以代码自身的文本格式运行它。

技术上,几乎所有 JavaScript 转换器都运用了一种叫做即时编译(just-in-time compiling)的技术;当 JavaScript 源代码被执行时,它会被编译成二进制的格式,使代码运行速度更快。尽管如此,JavaScript 仍然是一门解释型语言,因为编译过程发生在代码运行中,而非之前。

JavaScript能被浏览器进行解释,是一种解释性语言。受浏览器的影响,在不同的浏览器可能表现的效果不一样,存在浏览器差异。

  1. 与大多数编程语言不同,JavaScript 没有输入或输出的概念。它是一个在宿主环境(host environment)下运行的脚本语言,任何与外界沟通的机制都是由宿主环境提供的。

浏览器是最常见的宿主环境,但Node.js 的服务器端环境中也包含 JavaScript 解释器,所以JavaScript 也可用作服务器端语言。

  1. JavaScript的语法来源于 Java 和 C,所以这两种语言的许多语法特性同样适用于 JavaScript。
  2. JavaScript 是一种“动态类型语言”(弱类型数据语言),这意味着不需要指定变量将包含什么数据类型,如果你声明一个变量并给它一个带引号的值,浏览器就会知道它是一个字符串:
let myString = 'Hello';
//提供了一个函数typeof用于检测数据属于哪个类型
//1.typeof 变量名
//2.typeof(变量名)
typeof myString;
  1. JavaScript 通过原型链而不是类来支持面向对象编程 ,JavaScript 同样支持函数式编程。

函数也可以被保存在变量中,并且像其他对象一样被传递。


document.querySelector('html').onclick = function() {
    alert('别戳我,我怕疼。');
}

document.querySelector('html').addEventListener('click', () => {
  alert('别戳我,我怕疼。');
});

//Longhand
function add(num1, num2) {
   return num1 + num2;
}
//Shorthand 箭头函数
const add = (num1, num2) => num1 + num2;
//`匿名函数`: 因为它没有名字,匿名函数还有另一种我们称之为`箭头函数`的写法,箭头函数使用` () => `代替 `function ()`:

函数式编程思想:把操作尽量写成一系列嵌套的函数或者方法调用。

函数式编程特点:每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)

iOS小技能:链式编程在iOS开发中的应用https://blog.csdn.net/z929118967/article/details/75219317

1.3 JavaScript的使用场所

使用场所:任何的HTML页面、所有的动态页面,通过 DOM API动态修改 HTML 和 CSS 来更新用户界面(user interface)。

没有动态更新内容的网页叫做“静态”页面,所显示的内容不会改变。

  1. 前端验证,通过验证提高数据的完整性以及安全性。
<input type="submit" value="确定" class="guessSubmit">

const guessSubmit = document.querySelector('.guessSubmit');
guessSubmit.addEventListener('click', checkGuess);
function checkGuess() {
//...
}
  1. 操纵html元素,响应用户的各种操作,提高用户体验性。
<input type="text" id="guessField" class="guessField">

const guessField = document.querySelector('.guessField');
  guessField.focus();//让光标在页面加载完毕时自动放置于 <input> 输入框内,这意味着玩家可以马上开始第一次猜测,而无需点击输入框。提高了可用性,为使用户能投入游戏提供一个良好的视觉线。
  1. ajax核心技术之一

ajax: 在浏览器中运行的js脚本,通过http请求异步地访问服务器组件,服务器组件返回xml文件或者json格式的数据,js接收后通过解析xml或json来局部刷新页面,提高用户体验。

  1. 获取浏览器的一些相关信息

1.4 脚本调用策略

HTML 元素是按其在页面中出现的次序调用的,如果用 JavaScript 来管理页面上的元素(更精确的说法是使用 文档对象模型 DOM),若 JavaScript 加载于欲操作的 HTML 元素之前,则代码将出错。

  1. 内部 JavaScript的解决方案
//监听浏览器的 "DOMContentLoaded" 事件,即 HTML 文档体加载、解释完毕事件
//可能会带来显著的性能损耗
document.addEventListener("DOMContentLoaded", function() {
//  . . .
});
  1. 外部JavaScript的解决方案(推荐)

async 属性可以解决调用顺序问题,它告知浏览器在遇到<script>元素时不要中断后续 HTML 内容的加载。

不依赖于本页面的其它任何脚本时,async 是最理想的选择。

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

defer 属性,脚本将按照在页面中出现的顺序加载和运行:

<!--添加 defer 属性的脚本将按照在页面中出现的顺序加载-->
<script defer src="js/vendor/jquery.js"></script>

<script defer src="js/script2.js"></script>

<script defer src="js/script3.js"></script>

脚本调用策略小结:

  • 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async。
  • 如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中。

II 应用程序接口(Application Programming Interfaces)

在这里插入图片描述

  1. 第三方 API 并没有默认嵌入浏览器中,一般要从网上取得它们的代码和信息,比如地图 API 可以在网站嵌入定制的地图。

  2. 浏览器 API 内建于 web 浏览器中,它们可以将数据从周边计算机环境中筛选出来,还可以做实用的复杂工作,比如DOM API。

  • 文档对象模型 API(Document Object Model Application Programming Interfaces ) 能通过创建、移除和修改 HTML,为页面动态应用新样式等手段来操作 HTML 和 CSS。
  • 地_理位置 API(Geolocation API) 获取地_理信息。
  • 画布(Canvas) 和 WebGL API 可以创建生动的 2D 和 3D 图像。
  • HTMLMediaElementWebRTC 等影音类 API 。

2.1 BOM(Browser Object Model,浏览器对象模型)

整个浏览器窗口是一个顶层window对象

  1. 函数 alert() 警告框 prompt() 对话框 confirm() 确认框 window.open("URL"); setTimeout();超时之后调用目标函数 clearTimeout();超时之后清除目标函数 focus() 获得焦点 setInterval(,) (以毫秒计)调用执行函数/表达式 setInterval(code,millisec[,"lang"]) clearInterval() 取消对 code 的周期性执行,由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
//setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
//setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
window.setInterval(function() {}, 18000)//18S

前端小技能:利用action-type按钮事件实现批量删除 https://blog.csdn.net/z929118967/article/details/123222483

  1. 浏览器对象 Navigator对象
  1. 属性: appName、 appVersion 、 History 历史记录对象
  2. 函数:go(url);
  1. 地址栏对象 Location

属性:href 通过改变地址栏访问目标地址

  1. 文档对象 Document

2.2 DOM(Document Object Model,文档对象模型)

由W3C定义的一组规范一组API 用来操作HTML对象

  1. 直接获得标签对象

1) document.getElementById("id属性值") 通过ID来获得对应的标签对象

    <iframe id="ifr" width=400 height=500></iframe>
        document.getElementById("ifr").src = url[j];

2) document.getElementsByTagName("标签名称") 根据标签名称来获得一组标签,返回数组对象 3) document.getElementsByName("name属性值");根据name属性值来获得一组标签,返回数组对象

  1. 间接获得标签对象

1) 父标签.childNodes 获得当前标签的所有孩子节点,返回数组对象 2) 父标签.firstChild 获得第一个孩子节点 3) 父标签.lastChild 获得最后一个孩子节点 4) 标签.nextSibling 获得下一个兄弟节点 5) 标签.previousSibling 获得前一个兄弟节点

  1. 创建节点对象

1) document.createElement("标签名称") 创建一个对应的标签对象 2) document.createTextNode("文本值"); 创建一个文本节点对象

  1. 操作标签

1) 父标签.appendChild(子节点); 将一个标签追加到父标签当中 2) 父标签.removeChild(子节点); 删除子节点 3) 父标签.insertBefore(newElement,targetElement); 在目标元素之前插入一个新元素 4) 父标签.replaceChild(newElement,oldElement); 用新元素替换掉旧的元素

  1. 其他操作

1)标签.style.样式属性 = 属性值; h1.style.backgroundColor = "red"; 2) 标签.innerHTML 获得或者设置元素的标签体 3) 文本节点.nodeValue 获得文本节点值 4) 标签.parentNode 获得父节点 5) this 表示当前标签对象

see also

公众号:iOS逆向

demo: https://codepen.io/zhangkn/pen/BaLyerq?editors=1111

JavaScript小技能: Ajax

本文正在参加「金石计划」

I 术语

1.1 术语:箭头函数

匿名函数: 因为它没有名字

//Longhand
function add(num1, num2) {
   return num1 + num2;
}

匿名函数还有另一种我们称之为箭头函数的写法,箭头函数使用() =>代替 function ()

//Shorthand 箭头函数
const add = (num1, num2) => num1 + num2;

1.2 术语:回调函数和Promise链

  1. 回调函数是一个被传递到另一个函数中的会在适当的时候被调用的函数,如事件处理程序就是一种特殊类型的回调函数。

由于嵌套回调导致处理错误变得非常困难,代码也更难阅读和调试,所以JavaScript 中的异步编程是基于 Promise实现。

  1. Promise是一个由异步函数返回的可以向我们指示当前操作所处的状态的对象。在基于 Promise 的 API 中,异步函数会启动操作并返回 Promise 对象。然后你可以将处理函数附加到 Promise 对象上,当操作完成时(成功或失败),这些处理函数将被执行。

在 Promise 返回给调用者的时候,操作往往还没有完成,但 Promise 对象可以让我们操作最终完成时对其进行处理(无论成功还是失败)。

例子:fetch() 一个基于 Promise 的、用于替代 XMLHttpRequest 的方法。

const fetchPromise = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');

console.log(fetchPromise);
//将一个处理函数传递给 Promise 的 then() 方法。
fetchPromise.then( response => {
  console.log(`已收到响应:${response.status}`);
});

console.log("已发送请求……");

"pending" 状态意味着操作仍在进行中。

  1. Promise 链:链式使用 Promise
const fetchPromise = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');

fetchPromise
  .then( response => {
    return response.json();//json() 也是异步的,response.json() 返回的是 Promise对象
  })
  .then( json => {
    console.log(json[0].name);
  });

Promise 对象提供了一个 catch() 方法来支持错误处理。当异步操作成功时,传递给 then() 的处理函数被调用,而当异步操作失败时,传递给 catch() 的处理函数被调用。

如果将 catch() 添加到 Promise 链的末尾,它就可以在任何异步函数失败时被调用。于是,我们就可以将一个操作实现为几个连续的异步函数调用,并在一个地方处理所有错误。

const fetchPromise = fetch('bad-scheme://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');

fetchPromise
  .then( response => {
    if (!response.ok) {
      throw new Error(`HTTP 请求错误:${response.status}`);
    }
    return response.json();
  })
  .then( json => {
    console.log(json[0].name);
  })
  .catch( error => {
    console.error(`无法获取产品列表:${error}`);
  });

1.3 JSON(JavaScript Object Notation)

是一种轻量级的数据交换格式。它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。

  1. JSON用于在网站上表示和传输数据
////通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。
//创建一个新的 XMLHttpRequest 并监听它的 loadend 事件

request.open('GET', requestURL);
request.responseType = 'text'; // now we're getting a string!
request.send();
//以事件处理程序属性形式关联事件处理器
request.onload = function() {
  var superHeroesText = request.response; // get the string from the response
  var superHeroes = JSON.parse(superHeroesText); // convert it to an object
  populateHeader(superHeroes);
}
//通过DOM Level 2 Events 函数 addEventListener()关联事件处理器
  xhr.addEventListener('loadend', () => {
    log.textContent = `${log.textContent}完成!状态码:${xhr.status}`;
  });


  1. JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
//JSON 格式创建一个对象
   var oo = {
        name:"hello",
        age:123,
        getName:function(){
       return oo.name;
            }
       
    }

  1. JSON 和 JS 对象互转: 利用浏览器内建的 JSON进行转换数据
//parse(): 以文本字符串形式接受 JSON 对象作为参数,并返回相应的对象。
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}
//stringify(): 接收一个对象作为参数,返回一个对应的 JSON 字符串。
var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'

  1. 深拷贝多级对象
//深拷贝多级对象
const cloneObj = JSON.parse(JSON.stringify(obj));//JSON.stringify 对象的时候,包含 function, undefined or NaN 值的属性会从对象中移除。
//Shorthand for single level object
let obj = {x: 20, y: 'hello'};
const cloneObj = {...obj};
//————————————————
//版权声明:本文为CSDN博主「iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https://blog.csdn.net/z929118967/article/details/126142071

II Ajax(Asynchronous JavaScript and XML)

Ajax是一种在2005年由Google推广开来的编程模式,可以创建更好、更快以及更友好的web应用程序。

在这里插入图片描述Asynchronous JavaScript and XML基于javascript和HTTP请求,以异步地方式实现局部HTML的刷新。

2.1 从服务器获取数据

  1. XMLHttpRequest (通常缩写为 XHR)现在是一个相当古老的技术 - 它是在 20 世纪 90 年代后期由微软发明的,并且已经在相当长的时间内跨浏览器进行了标准化。

因为微软 Edge 浏览器的受宠, IE 已经没人管了

let request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'text';

request.onload = function() {
  poemDisplay.textContent = request.response;
};

request.send();

  1. Fetch API 基本上是 XHR 的一个现代替代品——它是最近在浏览器中引入的,它使异步 HTTP 请求在 JavaScript 中更容易实现
fetch(url).then(function(response) {
  response.text().then(function(text) {
    poemDisplay.textContent = text;
  });
});

2.2 缓存问题

如果是get方式请求,浏览器会将请求的数据缓存起来,如果下次访问的地址没变,浏览器就不会发送真正的请求,会将缓存的数据显示给用户。

解决方式:

  1. 可以在地址后面加上一个随机数。
  2. 使用post方式发送请求。
❌