一.1.通过var创建的全局变量(任何函数外的程序创建的)是不能被删除的;
2.无var创建的隐式全局变量(无视是否是在函数内外)是可以被删除的;
(由于JavaScript的两个特征,不自觉地创建出全局变量是出乎意料的容易。首先,你可以甚至不需要声明就可以使用变量;第二,JavaScript有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性。)
具体总结:
给一个非声明变量赋值会隐式创建一个全局变量(造成代码污染)(全局object的一个属性)。声明变量(Declared Variable)和非声明变量(Undeclared Variable)的区别是:
(1)声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。
(2)声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建。
(3)声明变量是它所在上下文环境的不可配置属性(non-configurable property),非声明变量是可配置的(例如非声明变量可以被删除)。
强烈建议必须使用声明变量!!!(无论是否在函数内)
二.
1.var声明的变量:
作用域是当前位置的执行上下文----一个函数内部或全局;
变量提升,任何位置var定义的变量,但都会被提升在代码执行前的最上方但不会被赋值;
2.let声明的变量:
作用域是块级域,在当前块级域里声明的变量只能在当前块级域里访问,与var不同,var声明的变量要么是全局的要么是函数内的无法是块级域的;
使用let在当前块级域里声明的变量,不会影响块级外面的相同名字的变量,这里的块级指的是"{ }";
3.用const创建一个只读常量,
可以创建在函数内也可以创建在块级内,之后任何的修改赋值都会引起错误;
一个常量不能和它所在相同作用域内的其他变量或函数拥有相同的名称。
常量可以是一个具体的值,也可以是一个对象。注意:常量是对象时,对象内部的属性可变而不受保护。
例如:
const myObject = {"key":"value"}; myObject = {"newKey":"newValue"}; // 这句报错 myObject.key = "newValue"; // 这句则会成功
三.数组中length属性设置或返回一个数组的长度
let a = [1, 2, 3, 4]; a.length = 3;//[1,2,3],长度为3 a.length = 5;//[1,2,3,empty*2],长度为3 a.length = 0;//[],长度为0 a.length = 2;//[empty*2],长度为2 console.log(a, a.length) var a1 = new Array(2); a1.aa = "as"; console.log(a1, a1.aa, a1.length);//[empty × 2, aa: "as"],as,长度为2 var a2 = ["b", "bb"]; var x = 'x'; // a2.x = "xx" a2[x] = "xx"; console.log(a2, a2[x], a2.length);// ["b", "bb", x: "xx"] "xx" 2
四.
1.原型链的工作原理
...每个函数都有一个prototype属性指向其函数原型;
...每个对象都有一个proto属性指向其构造函数原型;
...对象都是由函数创建的,函数也是由函数创建的;
...函数也是对象,只要是对象就有一个proto属性指向其构造函数;
...要访问一个对象的属性时,首先在其本身里查找,一直找到Object.prototype对象上,如果还没找到就返回空。
2.javascript的函数调用有几种方法
。。全局调用形如myfunction(),此时的this为window
。。通过事件调用,此时的this是事件目标元素
function cli() { console.log(this)//
。。对象调用函数,此时的this指的是此对象,形如obj.myFunction();
。。如果我们想在复制一个函数到一个方法时,可以使用call()、aplly()和bind()改变this指向
3.call()、apply()和bind()之间的区别
call()和apply()用法类似,传入的第一个参数都是要重载的那个对象,只不过apply()传入的第二个参数是一个数组形式;
bind()第一个参数传入重载对象,后面参数可选,最后返回一个函数。
1 function myFun(x, y, z) { 2 var name = "myName"; 3 console.log(this.name); 4 console.log(x); 5 console.log(y); 6 console.log(z); 7 }; 8 var obj = { 9 name: "OBJ"10 };11 myFun();//myNmae undefined undefined undefined12 myFun.call(obj, 12, 13)//OBJ 12 13 undefined13 myFun.apply(obj, [22, 33, 44])//OBJ 22 33 4414 var mybind = myFun.bind(obj, 55);15 console.log(mybind)//此时this已经指向了obj对象16 mybind(66, 77)//OBJ 55 66 77
五.new的机制
1 function People() { 2 this.name = 'I am People'; 3 this.age = 23 4 } 5 People.prototype.sayName = function () { return this }; 6 var obj = {};//先创建一个空的实例对象 7 obj.__proto__ = People.prototype; 8 People.call(obj); 9 console.log(obj.name, obj.sayName())//等价于var obj2 = new People; 10 //console.log(obj2.name, obj2.sayName())
七.js延迟加载的几种方式
1.使用defer和async这两种方法都有缺点。
defer属性标注的脚本是延迟脚本,使得浏览器延迟脚本的执行,也就是说,脚本会被异步下载但是不会被执行,直到文档的载入和解析完成,并可以操作,脚本才会被执行 ,执行时会按代码在文档里的
出现顺序执行。deferl浏览器支持的情况不一。
async属性标注的脚本是异步脚本,即异步下载脚本时,不会阻塞文档解析,但是一旦下载完成后,立即执行,阻塞文档解析.
2.动态创建script标签
1 //这些代码应被放置在标签前(接近HTML文件底部) 2
或者
1 window.onload = function () { 2 setTimeout(function () { 3 4 // reference to 5 var head = document.getElementsByTagName('head')[0]; 6 7 // a new CSS 8 var css = document.createElement('link'); 9 css.type = "text/css"; 10 css.rel = "stylesheet"; 11 css.href = "new.css"; 12 13 // a new JS 14 var js = document.createElement("script"); 15 js.type = "text/javascript"; 16 js.src = "new.js"; 17 18 // preload JS and CSS 19 head.appendChild(css); 20 head.appendChild(js); 21 22 // preload image 23 new Image().src = "new.png"; 24 25 }, 1000); 26 };
.以上方法延迟加载的脚本里都不能有,那些页面正常加载需要依赖的javascript代码,而可以把javascript代码分成两组。
八.经典小题封装函数
1.编写一个能将给定非负整数列表中的数字排列成最大数字的函数。例如,给定[50,2,1,9],最大数字为95021。
1 var arr = [53, 2, 1, 9]; 2 function change(arr) { 3 var ar = [], empty = 0, ar2 = [], max = 0; 4 var ar1 = arr.join('').split(''); 5 for (let i = 0; i < ar1.length; i++) { 6 for (let j = i + 1; j < ar1.length; j++) { 7 ar2 = ar1; 8 empty = ar2[j]; 9 ar2[j] = ar2[i];10 ar2[i] = empty;11 ar.push(ar2.join('') * 1);12 }13 };14 max = ar[0];15 for (let i = 0; i < ar.length; i++) {16 if (max < ar[i]) { max = ar[i] }17 }18 return max;19 } console.log(change(arr));
2.
1 var arr1 = ['a', 'b', 'c'], arr2 = [1, 2, 3];2 function exchange(arr1, arr2) {3 var arr = [];4 for (let i = 0; i < arr1.length; i++) {5 arr.push(arr1[i]); arr.push(arr2[i]);6 }7 return arr;8 }9 console.log(exchange(arr1, arr2));
3.用代码表示组合公式
1 function rank(n, m) { 2 if (m > n) { return false }; 3 var N = 1, M = 1, N_M = 1, sum = 1; 4 for (let i = n; i >= 1; i--) { 5 N *= i; 6 } 7 for (let i = m; i >= 1; i--) { 8 M *= i; 9 }10 for (let i = n - m; i >= 1; i--) {11 N_M *= i;12 }13 return N / M * N_M;14 }15 console.log(rank(4, 3));
4.经典的斐波那契数列
1 var n = prompt("请输入一串数列");2 function fbn(n) {3 var arr = [];4 for (var i = 0; i < n; i++) {5 i <= 1 ? arr.push(i) : arr.push(arr[i - 1] + arr[i - 2])6 };7 return arr;8 } console.log(fbn(n));
5.让人难忘的题考查很多知识点
1,this的指向
2,原型(prototype)以及原型链
3,继承
4,引用
要解出这道题,要理解以下几句话就可以了:
1,每一个构造函数,都有一个原型[[prototype]]属性 指向构造函数的原型对象
2,每一个实例生成的时候,都会在内存中产生一块新的堆内存
3,每一实例都有一个隐式原型__proto__ 指向构造函数的原型对象
4,this的指向 取决于this调用时的位置, 在这道题中, 也可以简单理解为, 谁调用方法, this就指向哪个对象
5,数组和字面量对象 都是 引用
6,原型链的查找规则: 就近原则
-
当实例上存在属性时, 用实例上的
-
如果实例不存在,顺在原型链,往上查找,如果存在,就使用原型链的
-
如果原型链都不存在,就用Object原型对象上的
-
如果Object原型对象都不存在, 就是undefined
1 function Parent() { 2 this.a = 1; 3 this.b = [1, 2, this.a]; 4 this.c = { demo: 5 }; 5 this.show = function () { 6 console.log(this.a, this.b, this.c.demo) 7 } 8 } 9 function Child() {10 this.a = 2;11 this.change = function () {12 this.b.push(this.a);13 this.a = this.b.length;14 this.c.demo = this.a++;15 }16 }17 Child.prototype = new Parent();18 var parent = new Parent();19 var child1 = new Child();20 var child2 = new Child();21 child1.a = 11;22 child2.a = 12;23 parent.show();//1,[1,2,1],524 child1.show();//11,[1,2,1],525 child2.show();//12,[1,2,1],526 child1.change();27 child2.change();28 parent.show();//1,[1,2,1],529 child1.show();// 5 [1, 2, 1, 11, 12] 530 child2.show();// 6 [1, 2, 1, 11, 12] 5
6.输入一个目标数值,在给定数列中找到最接近这个目标值的几个数的想加
1 var n = prompt("请输入一个目标数字值") 2 function ss(n) { 3 var arr = [-1, 2, 1, 4], 4 arrN = [{ key: "-1+2+1", value: 2 }, 5 { key: "-1+2+4", value: 5 }, 6 { key: "-1+1+4", value: 4 }, 7 { key: "2+1+4", value: 7 } 8 ], 9 arrJ = [], arrM = [];10 arrN.map(function (el) {11 arrJ.push({ "key": el.key, "valueY": el.value, "value": n * 1 - el.value });12 arrM.push(Math.abs(n * 1 - el.value));13 });14 console.log(arrJ, arrM);15 arrM.sort(function (a, b) { return a - b });16 console.log(arrM)17 for (let index = 0; index < arrJ.length; index++) {18 if (Math.abs(arrJ[index].value) == arrM[0]) {19 console.log("返回值:", arrJ[index].valueY, "因为:", arrJ[index].key)20 break;21 }22 }23 } ss(n);
7.编写一个在1,2,…,9(顺序不能变)数字之间插入+或-或什么都不插入,使得计算结果总是100的程序,并输出所有的可能性。例如:1 + 2 + 34 – 5 + 67 – 8 + 9 = 100。
1 var m = ['+', '-', ''];//每个数字之间可用的三种情况 2 for (var i = 0; i < Math.pow(3, 8); i++) { //每个空有3种方案,共有8个空,所以共有3的8次方(6561)个方案 3 var str = i.toString(3); //转成3进制,0表示+,1表示-,2表示'' 4 var method = "00000000".substr(0, 8 - str.length) + str;//没有的地方拼上0 5 console.log(method); 6 var fir = 1; 7 for (var j = 0; j < 8; j++) { 8 fir += m[method[j]] + (j + 2); //循环拼接字符串 9 }10 if (eval(fir) === 100) { //eval执行字符串得到结果11 console.log(fir + '=100')//打印可行方案12 }13 }