变量声明
变量的声明与赋值是需要分解开的。
变量的声明使用单一的var即可。
变量的命名规则
- 变量名必须以英文字母、_、$开头
- 变量名可以包括英文字母、_、数字
- 不可以用系统的关键字、保留字作为变量名
值类型
js中的值类型也就是数据类型,分为不可改变的原始值(栈数据)和引用值(堆数据)
原始值
number string boolean undefined null
引用值
array object function date regexp
区别
原始值是存放到栈里面,栈的特点是先入后出。
引用值是存放在堆里面,堆里面存放的是内容,栈里面存放的是堆的地址。
斐波那契数列
var n = parseInt(window.prompt('input'));
var first =1,second=1,third;
if(n>2){
for(var i=0;i<n-2;i++){
third=first+second;
first=second;
second=third;
}
document.write(third);
}else{
document.write(1);
}
求100以内的所有的质数
var count=0;
for(var i=1;i<100;i++){
for(var j=1;j<=i;j++){
if(i%j==0){
count++;
}
if(count==2){
document.write(i+" ");
}
count=0;
}
}
break与continue
break只能是放到循环语句中,如果放到别的地方,就会报错
continue是停止本次循环,进行下次循环
编程的两种形式
面向过程和面向对象
函数声明的三种方式
function 函数名(){}
var 变量=function 函数名(){} //命名函数表达式
var 变量=function(){} //此函数为匿名函数表达式
函数形参与实参
形参的个数可以超过实参
没有赋值的形参是undefined
形参的个数可以小于实参
形参都放在arguments中
形参的个数:arguments.length
实参的个数:函数名.length
当形参和实参个数相同的时候
则实参和arguments相映射,一一对应,你变我也变
但是当个数不相同的时候,特别是实参个数少于形参的时候,则不会改变没有形参参数的数值
获取字符串的每一位的两种方式
str[0]
str.charAt(0)
字符串反转:123转为仨俩壹
function reverse(){
var num = window.promt('input');
var str="";
for(var i=num.length-1;i>=0;i--){
str+=transer(num[i]);
}
document.write(str);
}
function transfer(target){
switch(target){
case "1":
return '壹';
case "2":
return '俩';
case "3":
return "仨";
}
}
解题思路:
输入数字成为字符串,通过字符串的遍历进行逆序,然后通过一个转化为汉字的函数进行转化。
利用递归写阶乘函数
function jc(n){
if(n == 1){
return 1;
}
return n*jc(n-1);
}
使用递归需要注意两点:
- 找规律;
- 找出口;
利用递归写斐波那契数列
function fb(n){
if(n==1||n==2){
return 1;
}
return fb(n-1)+fb(n-2);
}
js运行三部曲
- 语法分析
- 预编译
- 解释执行
预编译
函数声明整体提升,变量 声明提升
** imply global 暗示全局变量: ** 即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。
** 一切声明的全局变量,都是window的属性 **。
预编译的四部曲:
- 创建AO对象
- 找形参和变量声明,将变量和形参名作为AO属性名,值为Undefined
- 将实参值和形参相统一
- 在函数体里面找函数声明,值赋予函数体
任何执行期上下文中的this都指向的是window,通过New关键字的方式,可以进行构造函数创建对象,改变this指向,this最终指向的是创建的对象。
for循环中出现闭包的情况
如果在for循环中,对每一个元素都绑定一个函数的话,则会形成闭包
function test(){
var arr = [];
for(var i=0;i<10;i++){
arr[i]=function(){
console.log(i);
}
}
return arr;
}
解决for循环中的闭包问题的方法就是使用立即执行函数
function test(){
var arr=[];
for(var i=0;i<10;i++){
(function(j){
arr[j]=function(){
console.log(j);
}
})(i)
}
}
几道大厂前端笔试题
2015年阿里笔试题
function test(){
var liCollection = document.getElementsByTagName('li');
for(var i=0;i<liCollection.length;i++){
(function(j){
liCollection[j].onclick=function(){
console.log(j);
}
}(i))
}
}
** 利用立即执行函数解决for循环中的闭包问题 **
写一个方法,求一个字符串的字节长度
function retByteslen(target){
var count = target.length;
for(var i=0;i<target.length;i++){
if(target.charCodeAt(i)>255){
count ++;
}
}
}
** 解题思路: **汉字的字符比英文的字符多1个;
提取变量后可以如下编写函数:
构造函数的内部原理
- 在函数体最前面隐式的加上this={}
- 执行 this.xxx=xxx;
- 隐式的返回this
原型
- 定义:原型是function 对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
- 利用原型的特点和概念,可以提取共有属性。
- 对象如何查看原型,可以通过 proto
- 对象如何查看对象的构造函数 constructor
- 构造函数有个自带的属性叫prototype,也就是原型,是一个对象。
构造函数的最终级别的原型就是 Object.prototype
绝大多数的对象最终都会继承自Object.prototype
,也有可能是Null;
创建对象的方式可以是:Object.create(原型)
;
创建对象的两种方式
var obj={};//通过对象字面量的方式进行创建
var obj1=new Object();
js有精度问题,所以一般都不处理数据
call/apply
作用:改变this指向
区别:后面传的参数形式不同
第一个参数都是改变后的this,如果传的是null,则不改变this指向
call后面的参数是一个个进行传递的,apply后面的参数是一个arguments数组
继承的问题
function inherit(Target,Origin){
function F(){};
F.prototype=Father.prototype;
Son.prototype=new F();
}
Father.prototype.lastName="Deng";
function Father(){};
function Son(){};
通过中间变量的形式可以解决共用原型链的问题
通过中间变量解决共用原型链的模式叫:“圣杯模式”;
圣杯模式(解决共用原型的问题)
function inherit(Target,Origin){
function F(){};
F.prototype=Origin.prototype;
Target.prototype=new F();
Target.prototype.constructor=Target;
Target.prototype.uber=Origin.prototype;
}
Father.prototype.lastName='Deng';
function Father(){}
function Son(){}
inherit(Son,Father);
var son = new Son();
var father = new Father();
命名空间
管理变量,防止污染全局,适用于模块发开发
利用闭包实现变量私有化,可以实现命名空间,防止变量污染
原生javascript实现仿jquery链式调用
var deng = {
smoke:function(){
console.log('smoking');
return this;
},
dirnk:function(){
console.log('dirnking');
return this;
},
perm:function(){
console.log('preming');
return this;
}
}
deng.smoke().dirnk();
通过return this的方式,可以保证每个函数都可以返回自身对象,所以都可以链式调用的。
对象的枚举
for in
hasOwnProperty
这个是对象原型链上的判断属性是不是它自己的一个方法
in
判断这个属性名是不是当前对象的,如果是对象原型上面的,也会返回true的
instanceof
A instanceof B
A对象是不是B构造函数构造出来的 等同于 A对象的原型链上有没有B的原型
判断对象和数组的三种方法
可以通过Instanceof进行数组和对象的判断
[] instanceof Array
结果是true;
{} instanceof Array
结果是false;
可以通过constructor进行数组和对象的判断
[].contructor
结果是Array;
{}.constructor
的结果是Object;
可以通过toString()方法进行数组和对象的判断
Object.prototype.toString.call([])
结果是 “[object Array]”;
Object.prototype.toString.call({})
结果是“[object Object]”;
Object.prototype.toString.call(123)
结果是"[object Number]"
this
- 函数预编译过程中 this指向的是window
- 全局作用域里 this指向的是window
- call/apply可以改变函数运行时的this指向
- obj.func() func()里面的this指向obj
callee与caller
立即执行函数,执行完成后,函数会销毁,所以此时可以通过arguments.callee的方式进行调用递归函数
var num = (function (n){
if(n==1){
return 1;
}
return n * 阶乘(n-1);//此时的阶乘可以用 arguments.callee(n-1);来代替
}(100))
function test(){
demo();
}
function demo(){
console.log(demo.caller);
}
test();
深度克隆
function deepClone(origin,target){
var target = target || {},
toStr = object.prototype.toString,
arrStr="[object Array]";
for(var prop in origin){
if(origin[prop] !== "null" && typeof(origin[prop])=="object"){
target[prop]=toStr.call(origin[prop])==arrStr?[]:{};
deepClone(origin[prop],target[prop]);
}else{
target[prop]=origin[prop];
}
}
return target;
}
自己写一个push方法
var arr = [1,2,3];
Array.prototype.push=function(){
for(var i =0;i<arguments.lengthd;i++){
this[this.length]=arguments[i];
}
return this.length;
}
数组的几个方法
push是可以在数组的最后一位进行添加,可以添加多个参数。
pop是可以把数组的最后一位剪切出去,传递参数也是没有用的,只会截取最后一位。
unshift是在数组的前面进行添加,可以传递参数,也可以传递多个参数。
shift是在数组的前面进行删除,传递参数也是没有用的。
slice是数组的截取方法,是从第一个参数位开始截取,截取到第二个参数位为止。如果只有一个参数的话,则截取到数组的最后一位。
sort是进行数组的排序,最终会改变原数组。此处的排序不会完全按照数字大小进行排序,是ASCII码。
splice是截取,有三部分参数,第一个参数是从第几位开始,第二个参数是截取多少的长度,第三个以及后面的参数是在数组切口处添加的新的数据。
自己写一个splice方法
splice = function (pos){
pos += pos>0?0:this.length;
}
自己写一个sort方法
arr.sort(function(a,b){
return b-a;//降序排列
return a-b;//升序排列
})
给一个有序数组乱序
arr.sort(function(a,b){
return Math.random()-0.5;
})
按照字符串的长度进行排序
var arr = ['ad','ddd','ddddd']
arr.sort(function(a,b){
return a.length-b.length;//升序
});
for(var i=0;i<arr.length;i++){
for(var j=0;j<i;j++){
arr.sort(function(i,j){
return i.length - j.length;
})
}
}
按照字符串的字节长度进行排序
function retBytes(str){
var num = str.length;
for(var i=0;i<str.length;i++){
if(str.charCodeAt(i) > 255){
num ++;
}
return num;
}
}
var arr =['asdf','ddd','adgdasg'];
arr.sort(function(a,b){
return retBytes(a) - retBytes(b);
})
包装type函数
function type(target){
var template = {
"[object Array]":"array",
"[object Object]":'object',
"[object Number]":'numer - object',
"[object Boolean]":'boolean - object',
"[object String]":'string - object'
}
if(target === null){
return "null";
}
if(typeof (target) == "object"){
var str = Object.prototype.toString.call(target);
return template[str];
}else{
return typeof(target);
}else{
return ret;
}
}
在数组原型上面添加一个数组去重的方法
Array.prototype.unique=function(){
var temp={},
arr=[],
len=this.length;
for(var i=0;i<len;i++){
if(!temp[this[i]]){
temp[this[i]] = "abc";
arr.push(this[i]);
}
}
return arr;
}
str.split(“”)
字符串打散成数组
事件委托
var ul = document.getElementsByTagName('ul')[0];
ul.onmouseover = function (e){
var event = e|| window.event;
var target = event.target || event.srcElement;
target.style.backgroundColor="rgba(0,255,)"+target.getAttribute('img-date')+")";
target.setAttribute('img-date',parseInt(target.getAttribute('img-date'))+6);
}
元素的鼠标滑过事件,event target这两个参数兼容性的处理,e.target元素获取属性的方式是:
target.getAttribute('css属性')
,设置元素的属性:target.setAttribute('css属性',parsetInt(target.getAttribute('css属性')))
;
创建一个div并添加到body中(原生Js)
var newDiv = document.createElement('div');
document.body.appendChild(newDiv);
封装一个获取元素子节点的方法
var div = document.getElementsByTagName('div')[0];
function retElementChild(node){
var temp = {
length:0,
push:Array.prototype.push,
splice:Array.prototype.splice
},
child = node.childNodes,
len = child.length;
for(var i=0;i<len;i++){
if(child[i].nodeType === 1){
temp.push(child[i]);
}
}
return temp;
}