目次
1.3再帰的な古典的問題:フィボナッチ数列を再帰的に見つける
2.3ディープコピーとシャローコピーを区別するにはどうすればよいですか?
2.3.1浅いコピー:参照のみがコピーされ、相互間の操作は相互に影響します
2.3.2ディープコピー:ヒープ内のメモリを再割り当てします。異なるアドレスは互いに影響しません
1つは、再帰
1.1コンセプト
簡単に言うと、再帰とは、プログラムが次の図のように自分自身を呼び出すことを意味します。私たちがよく耳にする小僧の話と同じように、以前は山と寺院が山の中にありました。ある寺院には老僧と小僧がいました。ある日、その老僧は小僧に話をしました。物語の内容は、かつて僧がいたことです。山の中にはお寺があります。お寺にはお坊さんとお坊さんがいます。ある日、お坊さんがお坊さんに話をします。話の内容は、昔々山があったとき、お寺があり、お寺が…
JavaScriptの再帰は、関数内で関数自体を呼び出すことです。
// 递归:函数中调用函数自己
function f1() {
console.log("从前有座山,山里有座庙,庙里有个老和尚和一个小和尚,有一天老和尚对小和尚讲故事,故事内容是:");
f1();
}
f1();
1.2エクスポート
プログラムがこのように自分自身を何度も呼び出し続ける場合、それは決して終わらない、それは意味のない無限ループです。したがって、再帰の終了条件、つまり再帰の終了を定義する必要があります。条件が満たされない場合、再帰は継続して継続的に呼び出され、境界条件が満たされると、再帰的に戻ります。
// 递归的结束条件为i大于5
var i = 0;
function f1() {
i++;
if (i > 5) {
return;
}
console.log("从前有座山,山里有座庙,庙里有个老和尚和一个小和尚,有一天老和尚对小和尚讲故事,故事内容是:");
f1();
}
f1();
再帰の適用は通常、大きくて複雑な問題を元の問題に似た小さな問題に変換することで解決します。これは、以下のキューにいる人の数を数える問題と同じです。
上記の若い女性は、最初に並んだ人に何人並んでいるかを尋ね、最初の人は次のように答えました:I(1人)+後ろの人、若い女性は具体的な答えを得ませんでしたが、彼女は最初の人を理解している限りそれを知っていました個人の後ろに並んでいる人の数+最初の人は列の人の数なので、彼女は後ろの人に尋ね続け、同じ答えを得たので、彼女が得た答えは1 + 1 +人の後ろになった。それで、彼女はこのように尋ね続ける必要があり、最後の人に尋ねると、最後の人が答えました、そして私は一人でした。この瞬間、ミス・シスターは最終的に彼女が望んだ答えを得ました:1 + 1 +····· ···+ 1。上記は古典的な再帰の例ですが、ここでの再帰の終了条件は、最後の人かどうかということです。最後の人でない限り、質問してください。
1.3再帰的な古典的問題:フィボナッチ数列を再帰的に見つける
ゴールデンセクションシーケンスとも呼ばれるフィボナッチシーケンス(フィボナッチシーケンス)は、このようなシーケンスを指します:0、1、1、2、3、5、8、13、21、34、...、3番目の項目から最初、このシーケンスの各アイテムは、最初の2つのアイテムの合計と等しくなります。数学的には、フィボナッチ数列は再帰的な方法で定義されます:F(0)= 0、F(1)= 1、F(2)= 1、F(n)= F(n-1)+ F(n-2)(n> = 2、n∈N*)。次のアニメーションは、再帰的方法を使用してフィボナッチ数列の8番目の項、つまりF(7)を見つける方法を示しています。F(7)= F(6)+ F(5)の定義によれば、F(7)はF(6)とF(5)を知るだけでよく、F(6)= F(5)+ F( 4)、F(5)= F(4)+ F(3)...など、F(0)= 0、F(1)= 1が既知であるため、最初の項と第2項は終了できます。つまり、再帰の終了条件はn = 0またはn = 1です。
フィボナッチ数列のJSコード実装を再帰的に検索します。
// 递归:求斐波那契数列
function getFib(x) {
if (x == 0) {
return 0
} else if (x == 1) {
return 1
}
return getFib(x - 1) + getFib(x - 2);
}
console.log(getFib(7)); // >13
1.4再帰的な古典的問題:階乗を再帰的に見つける
nの階乗は、1からnへの乗算です。つまり、1 * 2 * 3 * ... *(n-1)* n、つまり、n!= 1 * 2 * 3 * ... *(n-1)です。 * n = n *(n-1)!、および(n-1)!= 1 * 2 * 3 * ... *(n-1)=(n-1)*(n-2)!、。 ..... n = 1、1の場合の類推。= 1 * 0!= 1、つまり、再帰の終了条件は1です。したがって、階乗関数factorial()を再帰的に見つけるアルゴリズムは次のとおりです。
再帰的な階乗JSコードの実装:
// 递归案例:求5的阶乘
function factorial(x) {
if (x == 1) {
return 1
}
return x * factorial(x - 1);
}
console.log(factorial(5)); // >120
1.5数値の数字の合計を再帰的に見つける
// 递归案例:求一个数字各个位数上的数字的和: 123--->1+2+3=6
function getEverySum(x) {
if (x < 10) {
return x;
}
// 获取的是这个数字的个位数
return x % 10 + getEverySum(parseInt(x / 10));
}
console.log(getEverySum(123)); // >6
1.6 DOMツリーを再帰的にトラバースする
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>递归遍历DOM树:公众号AlbertYang</title>
</head>
<body>
<h1>遍历 DOM 树</h1>
<div>
<ul>
<li>123</li>
<li>456</li>
<li>789</li>
</ul>
<div>
<div>
<span>haha</span>
</div>
</div>
</div>
<div id="demo_node">
<ul>
<li>123</li>
</ul>
<p>hello</p>
<h2>world</h2>
<div>
<p>dsa</p>
<h3>
<span>dsads</span>
</h3>
</div>
</div>
</body>
<script>
// 获取页面中的根节点---根标签
var root = document.documentElement; // html
// 函数遍历DOM树
function forDOM(root1) {
// 获取根节点中所有的子节点
var children = root1.children;
// 调用遍历所有子节点的函数
forChildren(children);
}
// 把这个子节点中的所有的子节点显示出来
function forChildren(children) {
// 遍历所有的子节点
for (var i = 0; i < children.length; i++) {
// 每个子节点
var child = children[i];
// 显示每个子节点的名字
f1(child);
// 判断child下面有没有子节点,如果还有子节点,那么就继续的遍历
child.children && forDOM(child);
}
}
//函数调用,传入根节点
forDOM(root);
function f1(node) {
console.log("节点的名字:" + node.nodeName);
}
</script></html>
ツートンコピー
JSのデータ型は、基本型と参照型の2つの型に分けることができます。基本的なタイプはundefined、null、Boolean、String、Number、Symbolなどであり、参照タイプはObject、Array、Date、Function、RegExpなどです。基本型の値は、メモリ内で固定サイズを占め、スタックメモリに格納されます。参照型の値はオブジェクトであり、ヒープメモリに格納されます。スタックは、オブジェクトの変数識別子とオブジェクトのストレージアドレスをヒープメモリに格納します。コピー方法の種類は異なります。基本タイプの場合、1つの変数から別の新しい変数に基本タイプの値をコピーすると、この値のコピーが作成され、そのコピーが新しい変数にコピーされます。参照型の場合、参照型の値をある変数から別の新しい変数にコピーすることは実際にはポインターであり、最後に両方の変数が同じオブジェクトを指します。
2.1浅いコピー
浅いコピーは直接コピーであり、オブジェクトのすべてのコンテンツを別のオブジェクトにコピーすることと同じです。基本タイプの場合、コピーは特定の値のコピーであり、参照タイプの場合はポインタをコピーします。
var obj1 = {
age: 18,
sex: "男",
car: ["奔驰", "宝马", "特斯拉"]
};
// 另一个对象
var obj2 = {};
// 把一个对象的属性复制到另一个对象中,浅拷贝
// 把a对象中的所有的属性复制到对象b中
function extend(a, b) {
for (var key in a) {
b[key] = a[key];
}
}
extend(obj1, obj2);
console.dir(obj2); // 开始的时候这个对象是空对象,复制之后有了obj1的属性
console.dir(obj1);
2.2ディープコピー
ディープコピーまたはコピー。基本タイプの場合、これは特定の値のコピーです。参照タイプの場合、オブジェクトの特定のプロパティまたはメソッドが見つかり、これに対応する新しいスペースが1つずつ別のオブジェクトにコピーされます。プロセスで再帰を使用する必要があります。
var obj1 = {
age: 18,
sex: "男",
car: ["奔驰", "宝马", "特斯拉"],
dog: {
name: "欢欢",
age: 3,
color: "黑白相间"
}
};
var obj2 = {}; //空对象
// 利用递归,把对象a中的所有的数据深拷贝到对象b中
function extend(a, b) {
for (var key in a) {
// 先获取a对象中每个属性的值
var item = a[key];
// 判断这个属性的值是不是数组
if (item instanceof Array) {
// 如果是数组,那么在b对象中添加一个新的属性,并且这个属性值也是数组
b[key] = [];
// 调用这个方法,把a对象中这个数组的属性值一个一个的复制到b对象的这个数组属性中
extend(item, b[key]);
} else if (item instanceof Object) { // 判断这个值是不是对象类型的
// 如果是对象类型的,那么在b对象中添加一个属性,是一个空对象
b[key] = {};
// 再次调用这个方法,把a对象中的属性对象的值一个一个的复制到b对象的这个属性对象中
extend(item, b[key]);
} else {
// 如果值是普通的数据直接复制到b对象的这个属性中
b[key] = item;
}
}
}
extend(obj1, obj2);
console.dir(obj1);
console.dir(obj2);
2.3ディープコピーとシャローコピーを区別するにはどうすればよいですか?
BがAをコピーするとします。Aが変更されたときに、Bが変更されるかどうかを確認します。Bが変更された場合は、浅いコピーを意味します。Bが変更されない場合は、深いコピーを意味します。浅いコピーでは、BはAの参照をコピーし、深いコピーでは、BはAのオントロジーをコピーします。上記のシャローコピーとディープコピーのコードでは、基本タイプの値はディープコピーですが、参照タイプの値の場合、シャローコピーはリファレンスをコピーし、ディープコピーは特定のオントロジーオブジェクトをコピーします。
2.3.1浅いコピー:参照のみがコピーされ、相互間の操作は相互に影響します
var obj1 = {
age: 18,
sex: "男",
car: ["奔驰", "宝马", "特斯拉"]
};
// 另一个对象
var obj2 = {};
// 把一个对象的属性复制到另一个对象中,浅拷贝
// 把a对象中的所有的属性复制到对象b中
function extend(a, b) {
for (var key in a) {
b[key] = a[key];
}
}
extend(obj1, obj2);
obj1.car[0] = "五菱宏光"; // 改变obj1中的值obj2也会改变
console.log(obj1);
console.log(obj2);
2.3.2ディープコピー:ヒープ内のメモリを再割り当てします。異なるアドレスは互いに影響しません
var obj1 = {
age: 18,
sex: "男",
car: ["奔驰", "宝马", "特斯拉"],
dog: {
name: "欢欢",
age: 3,
color: "黑白相间"
}
};
var obj2 = {}; //空对象
// 通过函数实现,把对象a中的所有的数据深拷贝到对象b中
function extend(a, b) {
for (var key in a) {
// 先获取a对象中每个属性的值
var item = a[key];
// 判断这个属性的值是不是数组
if (item instanceof Array) {
// 如果是数组,那么在b对象中添加一个新的属性,并且这个属性值也是数组
b[key] = [];
// 调用这个方法,把a对象中这个数组的属性值一个一个的复制到b对象的这个数组属性中
extend(item, b[key]);
} else if (item instanceof Object) { // 判断这个值是不是对象类型的
// 如果是对象类型的,那么在b对象中添加一个属性,是一个空对象
b[key] = {};
// 再次调用这个方法,把a对象中的属性对象的值一个一个的复制到b对象的这个属性对象中
extend(item, b[key]);
} else {
// 如果值是普通的数据直接复制到b对象的这个属性中
b[key] = item;
}
}
}
extend(obj1, obj2);
obj1.car[0] = "五菱宏光"; // 改变obj1中的值obj2不会改变
console.dir(obj1);
console.dir(obj2);
3つの要約
再帰とは、関数内で関数自体を呼び出すことです。再帰には終了条件が必要です。それ以外の場合は、無限ループになります。再帰の適用は、通常、大きく複雑な問題を元の問題と同様の小さな問題に変換することで解決します。JSでは、通常、再帰はディープコピー、メニューツリー、DOMトラバーサルなどの操作に適用されますが、再帰効率が非常に低いため、再帰を簡単に使用しないでください。
これで本日の研究は終わりです。私の能力と知識は限られているため、文章に問題がある場合は、私を批判して訂正してください。学び続け、改善したいなら、私に注意してください毎日、少しずつ学び、改善することがリーダーシップの始まりです。この記事があなたに役立つと思われる場合は、転送、コメントなどを歓迎します!!!