132、函数式编程的定义
1)它是一种范式,创建仅依赖输入就可以完成自身逻辑的函数。
2)保证当参数一样,函数被多次调用时,返回相同的值(引用透明性)
3)函数不会改变任何外部环境的变量,这将产生可缓存、可测试的、合理的、可并发执行的代码库
132、纯函数的定义
对给定的输入返回相同输出的函数。
133、高阶函数
1)高阶函数通常用于抽象通用的问题,即定义抽象
2)参数为函数或者返回值为函数,则为高阶函数
1)、forEach(arr,fn)的实现----Array元素的遍历
function forEach(arr,fn){
let i =0;
let len = arr.length;
for(;i<len;i++){
fn(arr[i]);
}
}
2)forEachObject(obj,fn)-----对对象的每一个属性遍历
function forEachObject(obj,fn){
for(let prop in obj){
if( obj.hasOwnProperty(prop) ){
fn(prop,obj[prop]);
}
}
}
3)every(arr,fn)
const every = (arr,fn)=>{
let ret=true;
for( let item of arr ){
ret = ret && fn(item);
}
return ret;
}
4)some(arr,fn)
const some = (arr,fn)=>{
let ret=false;
for( let item of arr ){
ret = ret || fn(item);
}
return ret;
}
134、闭包
在javascript中,闭包是指有权访问另一个函数内部变量的函数。
闭包有3个可访问的作用域:
1)在他自身内声明的变量
2)对全局变量的访问
3)对外部函数变量的访问
闭包可以记住上下文
1)tap函数----设计一个函数,该函数接受参数,并且返回值为函数,返回的函数也可以接受参数
调试参数
const tap = (val) => (fn)=>(typeof(fn)==='function'&&fn(value),console.log(val))
用途:遍历来自服务器的数组,数据错了,然后想调试数据
forEach([1,2,3],(a) => tap(a)( ()=>{console.log(a)} ) )
2)unary函数----将接受多个参数的函数转为只接受一个参数的函数
const unary = (fn) => fn.length === 1 ? fn : (arg) => fn(arg)
parseInt(parse,radix)
['1','2'].map(parseInt)//[1,NaN]
['1','2'].map( unary(parseInt) ) //[1,2]
3)once函数----只执行一次的函数,返回值为函数
const once = (fn)=>{
let done = false;
return function(...arg){
return done ? undefined : ((done=true),fn.apply(this,arg))
}
}
let doPay = once( ()=>{console.log('payment is done')} )
doPay(); //'payment is done'
doPay(); //undefiend
功能函数和遍历函数分开
投影 函数-----把函数应用于一个值并创建一个新值的过程称为投影
1)map(arr,fn)----用一个新数组捕获结果并返回该数组
const map = (arr,fn)=>{
let result = [];
for( let item of arr ){
result.push( fn(item) )
}
return result
}
//将每个元素平方
console.log(map([1,2,3],item => item*item)) //[1,4,9]
2)filter(arr,fn)-----过滤数组的内容,返回数组,成员是符合过滤条件的元素
const filter = (arr,fn) => {
let result = [];
for( let item of arr ){
fn(item) ? result.push(item) : undefined
}
result;
}
3)concatAll(arr,fn)-----将嵌套的数组转为一维数组,只能测试下面的形式的嵌套数组
const concatAll = (arr,fn)=>{
let result = [];
for(let item of arr){
result.push.apply(result,item);//使用apply将push的功能扩展为可以Push数组
}
return result
}
let arr = [[1,2],[3,4]]
console.log(concatAll(arr))
归约数组-----设置累加器并遍历数组(记住累加器的上一个值)以生成一个单一元素的过程
4)reduce(arr,fn,initVal)
const reduce = (arr,fn,initVal)=>{
let acc;
if( initVal != undefined ){
acc = initVal;
}else{
acc = arr[0];
}
if( initVal===undefined ){
for(let i =1;i<arr.length;i++){
acc = fn(acc,arr[i]);
}
}else{
for(let item of arr){
acc= fn(acc,item);
}
}
return [acc];
}
let arr = [1,2,3,4];
//数组元素相乘结果
console.log( reduce(arr,( acc,item )=>acc*item, 1) );
//数组元素相加
console.log( reduce(arr,( acc,item )=>acc+item, 0) );
5)zip(leftArr,rightArr,fn)-----对两个数组对应位置的元素进行处理,返回数组,数组元素是处理的结果
const zip = (leftArr,rightArr,fn)=>{
let result=[];
let len = Math.min(leftArr.length,rightArr.length);
for(let i=0;i<len;i++){
result.push(fn(leftArr[i],rightArr[i]));
}
return result;
}
//对两个数组对应位置元素相加,然后返回数组
let add = zip([1,2,3],[4,5,6],(val1,val2)=>val1+val2)
console.log(add);
135、柯里化与偏应用
柯里化的定义:柯里化就是把一个多参数的函数 转换为一个嵌套的一元函数的过程
curry(fn)=====返回值是一个函数或是一个函数的计算结果
const curry = (fn)=>{
if( typeof(fn) !=='function' ){
return Error('argument is error');
}
let len = fn.length;
return function curried(...arg){
if(arg.length<len){
return (...arg1)=>curried.apply(null,arg.concat(arg1))
}else{
return fn.apply(null,arg);
}
}
}
let sum = curry((a,b,c)=>a+b+c);
console.log(sum(1)(2,3));
偏应用---允许开发者部分应用函数参数
例如:setTimeout(fn,number)------在程序中隐藏掉number
136、组合---把一个函数的输出作为另一个函数的输入,数据流方向从右到左
输出:函数
组合两个函数
const compose = (a,b)=>c=>a(b(c))
//计算一个字符串中单词数量
const splitToArr = str => str.split(',')
const count = arr => arr.length
const wordNUmber = compose(count,splitToArr)
console.log(wordNUmber('hh,jjjj'))
组合多个函数----rest参数---接受多个参数 + reverse()----将数组元素倒序,数据流是从右到左 + reduce---将上次计算的结果作为下次计算的累加对象
注意:每个函数是一元函数
const reduce = (arr,fn,initVal)=>{
let acc;
if( initVal != undefined ){
acc = initVal;
}else{
acc = arr[0];
}
if( initVal===undefined ){
for(let i =1;i<arr.length;i++){
acc = fn(acc,arr[i]);
}
}else{
for(let item of arr){
acc= fn(acc,item);
}
}
return [acc];
}
const compose = (...arg)=>
(val) => reduce(arg.reverse(),(acc,fn)=>fn(acc),val)
const splitToArr = str => str.split(',')
const count = arr => arr.length
const wordNUmber = compose(count,splitToArr)
console.log(wordNUmber('hh,jjjj'))
136、管道(序列)---- 把一个函数的输出作为另一个函数的输入,数据流方向从左到右(和组合很像)
136、判断1-400有多少个1
如:1-23有13个1
1)将1-400转换成字符串变量str;
2)正则表达式匹配字符串中的1:reg=/1/g
3)使用arr = str.match(reg);匹配到的结果都保存在数组arr中
4)通过arr.length获得1的个数
let reg = /1/g;
let str = '';
for(let i=1;i<=13;i++){
str = str + i;
}
let arr = str.match(reg);
console.log(arr);
136、js中二进制和十进制的转换
十进制转换成二进制:num.toString(2)方法
二进制转换成十进制:parseInt(str,10)方法
parseFloat()会将小数点后面多余的0删掉
var num = 8; // 定义一个十进制的整数
var str = num.toString(2); // 转换成二进制的字符串,2表示二进制 "1000"
var num1 = parseInt(str, 2); // 将二进制转换成十进制的整数,2也表示二进制 8
console.log(num1);
扩展:将给定数字转换成二进制字符串。如果字符串长度不足 8 位,则在前面补 0 到满8位。
var num = 8;
var str = num.toString(2);
var l = str.length;
var s1 = "00000000"; //用于补齐,满8位
if(l < 8){
var s2 = s1.slice(0,8-l); //截取需要补齐的位数
str = s2 + str; //在前面进行补齐
}
console.log(str);
136、输入字符串包含a-z和0-9,要求输出的字符串:显示字母后是数字,且都是升序排列
思路:
1)通过str.match分别匹配到输入字符串中的字母和数字,存放在数组arr1和arr2中
2)如果arr1有值,则进行快速排序,然后把结果拼接在字符串变量result中
3)如果arr2有值,则进行快速排序,然后把结果拼接在字符串变量result中
function quickSort(arr){
let len = arr.length;
if(len<2){
return arr;
}
let mid = Math.floor(len/2);
let midEle = arr.splice(mid,1);
let left = [],
right = [];
for(let i=0;i<len-1;i++){
if(midEle>arr[i]){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort(left).concat(midEle,quickSort(right));
}
function sort(str){
let reg1 = /[a-z]/g;
let reg2 = /\d/g;
let arr1 = str.match(reg1);
let arr2 = str.match(reg2);
let result = '';
if(arr1){
arr1 = quickSort(arr1);
for(let val of arr1){
result += val;
}
}
if(arr2){
arr2 = quickSort(arr2);
for(let item of arr2){
result += item;
}
}
return result;
}
let str = 'agh1zz9sd0z' //adghszzz019
console.log(sort(str));
136、统计一个整数转成二进制之后含有1的个数
例:
9 的二进制序列:0000 1001
8 的二进制序列:0000 1000
按位与后的序列:0000 1000
此时count为1,n=8
8 的二进制序列:0000 1000
7 的二进制序列:0000 0111
按位与后的序列:0000 0000
此时count为2,即为9的二进制序列1的个数
// 二进制中1的个数,n = n&(n-1)
function NumberOfOneCount1( n)
{
let count=0;
while(n > 0)
{
count++;
n = n & (n-1);
}
return count;
}
console.log(NumberOfOneCount1(5));
136、输入两个整数,求两个整数二进制格式有多少个位不同
1)利用num.toString(2)转成二进制形式字符串str1,str2
2)如果str1与str2存在差别diff个,则cou=diff,否则cou=0
3)进行遍历,遍历次数为两个二进制字符串长度的最小值
4)每次遍历的时候进行比较:如果diff=0,则ind1=ind2=i;diff<0,则ind=i,ind2=i-diff;diff>0,ind1=i-diff,ind2=i;如果对应索引的字母相等则cou++
// 输入两个整数,求两个整数二进制格式有多少个位不同
let num1 = 188;
let num2 = 77;
let str1 = num1.toString(2);
let str2 = num2.toString(2);
let len1 = str1.length;
let len2 = str2.length;
let cou = 0;
let char1;
let char2;
let diff = len1-len2;
let diff2 = Math.abs(diff);
let ind1;
let ind2;
if(diff!==0){
cou = diff2;
}
for(let i =0;i<Math.min(len1,len2);i++){
if(diff===0){
ind1=i;
ind2=i;
}else{
if(diff>0){
ind1=diff2+i;
ind2=i;
}else{
ind1=i;
ind2=diff2+i;
}
}
char1 = parseInt(str1.charAt(ind1),10);
char2 = parseInt(str2.charAt(ind2));
if( char1!== char2){
cou++;
}
}
console.log(cou);
上面太复杂了,可以用先对num1和num2进行异或得num,然后统计num中二进制中1的个数
// 二进制中1的个数,num = num & (num-1);
function countOne(num1,num2)
{
let num = num1 ^ num2;
let reg = /1/g;
let arr = num.toString().match(reg);
return arr.length;
}
console.log(countOne(1,2));
136、两个数的最大公约数
//转辗相除法
用第一个数除以第二个数得到余数
如果余数为0,则第二个数就是最大公约数,返回
否则,用第二个数除以余数,回到步骤1(即将第二个数作为新的第一个数,将余数作为新的第二个数)
function gcd(m,n){
if(n===0){
return m;
}else{
return gcd(n,m%n);
}
}
console.log(gcd(6,9));
136、判断是否是素数---除1之外,只能被1和自身整除,2也是素数
1)先判断是否为偶数,为偶数就一定不是素数
2)那就判断奇数,奇数从3开始,每次迭代加2
3)迭代次数为数M的开平方,开平方是M的最大因子,最大因子之后的数对判断该数是否是素数没有意义
function sushu(m){
let i;
if(m % 2 == 0)
return false;
for( i=3;i<=Math.sqrt(m);i+=2)
if(m % i == 0)
return false;
return true;
}
console.log(sushu(9));
136、输入M,N,1<M<N<1000000,求区间[M,N]内的素数个数
function sushu(m){
let i;
if(m % 2 == 0)
return false;
for( i=3;i<=Math.sqrt(m);i+=2)
if(m % i == 0)
return false;
return true;
}
function countSuShu(m,n){
let count = 0;
if(m==2){
count++;
}
for(let i=m;i<=n;i++){
if(sushu(i)){
count++;
}
}
return count;
}
console.log(countSuShu(3,10));
136、求连续区间的最大值
思路:用一个变量max保存连续区间的最大值,变量sum存储对每个元素的求和,在这里需要判断sum<0的时候,sum=0,同时sum和max比较,max<sum则max=-sum
// 最大连续和
function maxContinueSum(arr){
let max = arr[0];
let sum = 0;
for(let val of arr){
sum += val;
if(sum<0){
sum=0;
}
if(max<sum){
max = sum;
}
}
return max;
}
console.log(maxContinueSum([1,2,4]));
变型:连续区间的和最小
136、求连续区间的回文
// 找出区间的回文数
function huiwen(a){
let b,c=0,d;
d = a;
while(d != 0){
b = parseInt(d%10); //b是d除以10的余数
c = c*10 + b; //c是c的10倍,加上d的余数
d = parseInt(d/10); //d是d除以10的商
}
if(c == a){ //c==a则为回文
console.log(a);
}
}
for(let i=100;i<124;i++){
huiwen(i); //101 111 121
}
137、给定一个未排序的数列,找出排序下的两个相邻值的最大差值,少于两个值时返回0
1)先排序
2)max保存最大差值,diff保存两数差值
3)每次迭代的时候,max<diff,则max=diff
例:[1,3,2,0,1,6,8],最大差值3
// 给定一个未排序的数列,找出排序下的两个相邻值得最大差值,少于两个值时返回0
// [1,3,2,0,1,6,8],最大差值3
function maxDiff(arr){
let len = arr.length;
let max = 0;
let diff;
if(len < 2){
return max;
}
arr.sort((a,b) => a-b);
for(let i=0;i<len-1;i++){
diff = arr[i+1]-arr[i]
if(max < diff ){
max = diff;
}
}
return max;
}
let arr = [1,3,2,0,1,6,8];
console.log(maxDiff(arr));
137、实现两个元素的交换
// 实现两个元素交换
function change(a,b){
console.log('primitive: ' + 'a= ' + a + ', b=' + b)
a ^= b;
b ^= a;
a ^= b;
console.log('change: ' + 'a= ' + a + ', b=' + b)
}
change(2,3);
138、实现数字的倒序
// 实现数字倒序
// -12345倒序后-54321
function reverse(num){
let temp = num;
let r = 0;
let t =temp;
t = t>0?t:-t;
for(;t;t = parseInt(t/10)){
r = r*10 + parseInt(t%10);
}
return temp>0?r:-r;
}
console.log(reverse(-123));
// 数字倒序
function reverse(num){
// 0-正数, 1-负数
let flag = 0;
if(num<0){
flag=1;
}
num= parseInt(Math.abs(num).toString().split('').reverse().join(''),10);
if(flag){
num = parseInt('-' + num,10);
}
return num;
}
console.log(reverse(-102));
139、大富翁游戏,玩家根据骰子的点数决定走的步数,即骰子点数为1时可以走一步,点数为2时可以走两步,点数为n时可以走n步。求玩家走到第n步(n<=骰子最大点数且是方法的唯一入参)时,总共有多少种投骰子的方法
1 (1) 1种
2 (11) (2) 2种
3 (111) (12) (21) (3) 4种
4 (1111) (112) (121) (212) (22) (31) (13) 8种
。。。。。。。。f(n) = 1 +f(n-1)+f(n-2)+...+f(1)
这是一个递归问题,想知道f(n) 必须查到栈底也就是f(1)所在,或者找出规律f(n) = 2^(n-1)
let n = parseInt(readline());
let result = 2**(n-1);
print(result);
140、给出一个非空的字符串,判断这个字符串是否是由它的一个子串进行多次首尾拼接构成的。输出最长的子串
例如,”abcabcabc”满足条件,因为它是由”abc”首尾拼接而成的,而”abcab”则不满足条件,否则输出false
思想如下:从最长的二等分开始查找,用等分后的子字符串拼接成新的字符串B,与原字符串A进行比较,如果相等,返回这个字符串,如果不相等进行三等分以此类推,如果直至n等分(n=字符串A长度)都不能满足,输出false
// 判断字符串是否由淄川构成
// 首先对父串str二分,然后获取二分前面的子串str1,父串长/子串长=n次数,将子串str1复制n次得到str2
// 对比str===str2,若相等输出str1,flag=true,break;
// 若不等,在对父串3等分,。。。,直至1等分,还是没有符合条件的,证明父串不是由子串构成
let str = 'a';
let len = str.length;
let tmpLen = parseInt(len/2-1);
let flag = false;
for(let i=tmpLen;i>=0;i--){
let str1 = str.slice(0,i+1);
let len1 = str1.length;
let str2='';
//for(let j=0;j<len/len1;j++){
//str2 += str1;
//}
str2 = str1.repeat(parseInt(len/len1))
if(str == str2){
console.log(str1);
flag = true;
break;
}
}
if(flag==false){
console.log(false);
}
140、给出一个正整数n,请给出所有的包含n个'('和n个')'的字符串,使得'('和')'可以完全匹配
例如:
'(())()','()()()' 都是合法的;
'())()('是不合法的
function process( str, res, nl,nr, n) {
// 若str中左括号的数量和右括号的数量都为n, 则将str添加到结果数组中, 并返回
// 结束条件
if (nl >= n && nr >= n) {
res.push(str);
return;
}
// 原则是先添加左括号
if (nl < n) { // 左括号的数量不超过n
str += '(';
// 注意process每次都添加括号并删除括号, 返回时对传入的str没有做改变
process(str, res, nl + 1, nr, n);
str = str.slice(0,str.length-1); // 删除push_back添加的左括号
}
// 若左括号的数量比右括号的多
if (nl > nr) {
str += ')'; // 补充一个右括号
process(str, res, nl, nr + 1, n);
str = str.slice(0,str.length-1);
}
}
let str ='';
let n = parseInt(readline());
let res = [];
process(str,res,0,0,n);
str = res.join(',');
print(str);
140、给出一个字符串str,请给出所有的移除序列的方法
function count(str){
let sum = 1;
let len = str.length;
let arr = [];
let chara = '';
for(let i=0;i<len;i++){
chara = str.charAt(i)
if( chara === '('){
arr.push(chara);
}else if( chara === ')' ){
sum *= arr.length;
arr.pop();
}
}
return sum;
}
print(count(readline()));
141、给定一个字符串,看是否匹配,如()(){}[]---匹配输出yes,如()[}}---输出no
function isMatch(str){
let stack = [],
dict = {"]":"[", "}":"{", ")":"("},
arr = ['[','{','('],
char = '';
for(char of str){
if( arr.includes(char) ){
stack.push(char);
}else if( stack==[] || dict[char] != stack.slice(stack.length-1,stack.length)){
return false
}else if( dict[char] == stack.pop() ){
continue;
}else{
return false;
}
}
return (stack.length==0) ? true : false
}
console.log(isMatch('{}'));
150、给出一个整数n,将n分解为至少两个整数之和,使得这些整数的乘积最大化,输出能够获得的最大的乘积
思路:分解出来的3越多,乘积越大,当分解到剩4及以下时,可以看出此时再分解结果会更小,因此不予分解,直接相乘即可
function process(num){
if(num=== 2){
return 1;
}else if( num ===3 ){
return 2;
}else if( num ===4 ){
return 4;
}
let res = 1;
while(num > 4){
res *= 3;
num -=3;
}
if(num !== 0){
return res*num;
}else{
return res;
}
}
console.log(process(12));
151、从非负整数序列 0, 1, 2, ..., n中给出包含其中n个数的子序列,请找出未出现在该子序列中的那个数
输入为n+1个非负整数,用空格分开。
其中:首个数字为非负整数序列的最大值n,后面n个数字为子序列中包含的数字
// 将所有的数字都遍历一遍,字符不在字符串里面就输出
let str = readline();
let arr = str.split(' ');
let m = parseInt(arr[0]);
for( let i=0;i<=m;i++){
if( !str.includes(i+'') ){
print(i);
}
}
152、小招喵喜欢在数轴上跑来跑去,假设它现在站在点n处,它只会3种走法,分别是:
1.数轴上向前走一步,即n=n+1
2.数轴上向后走一步,即n=n-1
3.数轴上使劲跳跃到当前点的两倍,即n=2*n
现在小招喵在原点,即n=0,它想去点x处,快帮小招喵算算最快的走法需要多少步?
注意:起点为0,输入的数字为正和为负的步数是一样的,所以当x<0的时候,只需要将x变为正数即可,后面只考虑正数的算法
思路:
一个动态规划问题,小猫有两种跳跃方式:1. 一次走一步,2.一次走2倍与当前位置的步数。
dp方程:
dp[i-1] = min(dp[i-1], dp[i/2])+1 (i % 2 == 0)
dp[i-1] = min(dp[i-1], dp[(i+1)/2+1] + 1 (i % 2 != 0)
当i为偶数时,对于跳跃方式2来说,可以从前一个位置直接跳过来,即dp[i/2]。
当i为奇数时,对于跳跃方式2来说,需要在前一个位置跳过来需要两步,先方式2后方式1.
function process(x){
if(x<0){
x = -x;
}
if(x<2){
return x;
}
let dp = [0,1];
for(let i = 2;i<=x;i++){
if(i%2==0){
dp[i] = Math.min(dp[i-1],dp[i/2])+1;
}else{
dp[i] = Math.min(dp[i-1],dp[(i+1)/2]+1)+1;
}
}
return dp[x];
}
print(process(parseInt(readline())));
153、给定一组非负整数组成的数组h,代表一组柱状图的高度,其中每个柱子的宽度都为1。 在这组柱状图中找到能组成的最大矩形的面积(如图所示)。 入参h为一个整型数组,代表每个柱子的高度,返回面积的值
// 分治法:最大矩形面积只可能有三种情况:
// 1. 取决于高度最小的柱子,此时面积等于高度乘总长度;
// 2. 最大面积出现在高度最小的柱子左边;
// 3. 最大面积出现在高度最小的柱子右边;
function largestArea(x){
let len = x.length;
let minNumIndex = x.indexOf((Math.min.apply(null,x)));
let val1 = x[minNumIndex] * len;
let val2,
val3;
if( minNumIndex != 0 ){
val2 = largestArea(x.slice(0,minNumIndex+1));
}else{
val2 = 0;
}
if( minNumIndex !=len-1 ){
val3 = largestArea(x.slice(minNumIndex+1));
}else{
val3 = 0;
}
return Math.max.call(null,val1,val2,val3)
}
let n = parseInt(readline());
let arr = readline().split(' ');
arr.forEach(function(val,key){
arr[key] = parseInt(val);
});
print(largestArea(arr));
154、f(x)表示把x这个数用十进制写出后各个数位上的数字之和。如f(123)=1+2+3=6。 g(x)表示把x这个数用二进制写出后各个数位上的数字之和。如123的二进制表示为1111011,那么,g(123)=1+1+1+1+0+1+1=6。 小明同学发现对于一些正整数x满足f(x)=g(x),他把这种数称为幸运数,现在他想知道,大于0且小于等于n的幸运数有多少个?
每组数据输入一个数n(n<=100000)
每组数据输出一行,小于等于n的幸运数个数
let n = parseInt(readline());
let result = 0;
let intNum;
for(let num=1;num<=n;num++){
if( count10Sum(num) === count2sum(num) ){
result++;
}
}
print(result);
function count10Sum(num){
let arr = num.toString().split('');
let sum = 0;
arr.forEach((item)=>{
sum += parseInt(item);
})
return sum;
}
function count2sum(num){
let str = num.toString(2);
let len = (str.match(/1/g)).length;
return len ? len : 0;
}
156、给你两个集合,要求{A} + {B}。 注:同一个集合中不会有两个相同的元素
每组输入数据分为三行,第一行有两个数字n,m(0 ≤ n,m ≤ 10000),分别表示集合A和集合B的元素个数。后两行分别表示集合A和集合B。每个元素为不超过int范围的整数,每个元素之间有个空格隔开
针对每组数据输出一行数据,表示合并后的集合,要求从小到大输出,每个元素之间有一个空格隔开,行末无空格
let nm = readline().split(' ');
let n = parseInt(nm[0]);
let m = parseInt(nm[1]);
let A = readline().split(' ');
let B = readline().split(' ');
A.forEach((item,key)=>{
A[key] = parseInt(item);
});
B.forEach((item,key)=>{
B[key] = parseInt(item);
});
let arr = A.concat(B);
let str ='';
arr = [...new Set(arr)];
arr.sort((a,b)=>a-b);
arr.forEach((item)=>{
str += item + ' ';
});
str = str.replace(/\s$/,'');
print(str);
尽管是一个CS专业的学生,小B的数学基础很好并对数值计算有着特别的兴趣,喜欢用计算机程序来解决数学问题,现在,她正在玩一个数值变换的游戏。她发现计算机中经常用不同的进制表示一个数,如十进制数123表达为16进制时只包含两位数7、11(B),用八进制表示为三位数1、7、3,按不同进制表达时,各个位数的和也不同,如上述例子中十六进制和八进制中各位数的和分别是18和11,。 小B感兴趣的是,一个数A如果按2到A-1进制表达时,各个位数之和的均值是多少?她希望你能帮她解决这个问题? 所有的计算均基于十进制进行,结果也用十进制表示为不可约简的分数形式
输入中有多组测试数据,每组测试数据为一个整数A(1 ≤ A ≤ 5000)
对每组测试数据,在单独的行中以X/Y的形式输出结果
输入:
5
3
输出:
7/3
2/1
let arr = readline().split(' ');
let result = []; //存放结果
arr.forEach((val,key)=>{
arr[key] = parseInt(val);
});
arr.forEach((val)=>{
result.push(count(val));
});
result.forEach((val)=>{
console.log(val);
})
function count(num){
let i =2;
let sum = 0;
let tmp;
for(;i<=num-1;i++){ //cong 2-num-1进制
let bus ;
let rem;
let tmpNum = num;
do{
bus = Math.floor(tmpNum/i);
rem = Math.floor(tmpNum%i);
if(rem!== 0){
sum += rem;
}
tmpNum = bus;
}while(tmpNum);
}
let mul = gcd(sum,num-2);
return (sum/mul) + '/' + ((num-2)/mul); //最简形式
}
function gcd(m,n){ //求最大公约数,辗转相除法
if(n===0){
return m;
}else{
return gcd(n,m%n);
}
}
数字num转为n进制的时候sum存转化为n进制后各个位上的值,利用商bus=parseInt(num/n),余数rem=parseInt(num%n),若rem!=0,则sum += rem;num=bus.
若果num!=0,则重复上面步骤。
sum是分子和,分母就是表示进制数的种类num-2.由于要求最简形式,所以还得求出最大公约数
最小公倍数 = n*m /最大公约数
匹配标点符号和空格:reg = /\s|\pP/; //空格或者标点符号
157、实现大数相加
// 大数相加,转化为字符串相加,从低向高做加法运算,既不会溢出,也不会损失精度
function sumStrings(a,b){
var res='', c=0; //---res存放结果,c保留进位
a = String(a).split('');
b = String(b).split('');
while (a.length || b.length || c){
// ~~a 转换成数字类型 ~~true = 1 ~~!undefined = 1
// ~~undefined = 0
// array pop()没有元素弹出则返回undefined
c += ~~a.pop() + ~~b.pop(); //将两个数转化为数字类型,进行相加
res = c % 10 + res; //两数相加的个位值:c % 10
c = c>9;
}
return res.replace(/^0+/,''); //将前缀0去掉
}
console.log(sumStrings('123','42'));
156、给定两个字符串,求最长公共子序列
// 找A、B序列的最长公共序列
//c[][]记录最长公共序列长度, b[][]记录最长公共序列长度的来源用1,2,3标志
// s1[i] == s2[j-1],i=0,...len1-1,j=1,...,len2,c[i][j] = c[i-1][j-1],b[i][j]=1
// s1[i]不等于s2[j-1],如果c[i][j-1]>=c[i-1][j],c[i][j] = c[i][j-1],b[i][j]=2
// 如果c[i][j-1]< c[i-1][j],c[i][j] = c[i-1][j],b[i][j]=3
// 记录最长公共序列长度=c[len1][len2]
// 最优结果则是倒序,b[i][j]=1,则s1[i-1]=s2[j-1],输出s1[i-1]
// 接着在看b[i-1][j-1]直到i=0或者j=0才结束,递归
// 实现c[][] b[][]
function LCSL(str1,str2,c,b){
let len1 = str1.length,
len2 = str2.length,
i = 0,j,
tmp = [];
// 初始化c[][] len1+1行 len2+1列
for(;i<=len2;i++){ //初始化第一行全为0
tmp.push(0);
}
c.push(tmp);
b.push(tmp);
for(i=0;i<len1;i++){
c.push([0]); //第一列全为0
b.push([0]);
}
for( i=1;i<=len1;i++ ){//控制str1序列
for(j=1;j<=len2;j++){//控制str2序列
if( str1[i-1] == str2[j-1] ){
//如果当前字符相同,则公共子序列的长度为该字符前的最长公共子序列+1
c[i][j] = c[i-1][j-1]+1;
b[i][j] = 1;
}else{
if( c[i][j-1] >= c[i-1][j] ){
c[i][j] = c[i][j-1];
b[i][j] = 2;
}else{
c[i][j] = c[i-1][j];
b[i][j] = 3;
}
}
}
}
}
// 求解最优解
function show(str1,b,i,j){
// 递归结束条件
if( i==0 || j == 0 ){
return;
}
if( b[i][j] == 1){
//递归
show(str1,b,i-1,j-1);
// 输出
console.log(str1.charAt(i-1));
}else if( b[i][j] == 2 ){
//递归
show(str1,b,i,j-1);
}else{
//递归
show(str1,b,i-1,j);
}
}
let str1 = 'ABCADAB',
str2 = 'BACDBA',
len1 = str1.length,
len2 = str2.length,
c=[],
b=[];
LCSL(str1,str2,c,b)
console.log(c,b);
console.log('最长序列长度:' + c[len1][len2]);
console.log(show(str1,b,len1,len2)); //递归构造最长公共序列最优解
156、石头合并问题---路边玩法(直线)和操场玩法(圆)
// a[i]第i堆石子的数量
// sum[i]前i对石子的重量(a1,...,ai)
// min[i][j] max[i][j]记录第i堆到第j堆(ai,...,aj)的 重量=sum[j]-sum[i-1]
// 初始化,min[i][i]=0,max[i][i]=0,sum[0]=0,计算sum[i],i=1,...n
// 按照公式,循环
// 计算2堆合并的,3对合并,...,n堆合并最小(大)花费
// 最优解:min[1][n] max[1][n]
let min = [],
max = [],
tmp1 = [], //用于初始化min[i][i],max[i][i]
tmp2 = [],
sum = [], // sum[i]前i对石子的重量(a1,...,ai)
min_circular,
max_circular;
let n = 6; //对数
let a = [0,5,8,6,9,2,3];//每堆的重量
// straight(a,n); //直线
// console.log(min[1][n],max[1][n]);
circular(a,n); //环线
console.log(min_circular,max_circular);
function straight(a,n){
// 初始化 min[i][i]=0,max[i][i]=0
for(let i=0;i<=n;i++){
tmp1 = [];
tmp2 = [];
tmp1[i] = 0;
tmp2[i] = 0;
min.push(tmp1); //这里要注意,不能讲同一个数组作为min max的元素,这样的话就会共享内存
max.push(tmp2)
}
// 前0堆石子重量为0
sum[0] = 0;
// 计算前i堆石子的重量
for( let i=1;i<=n;i++ ){
sum[i] = sum[i-1] + a[i];
}
// 按照公式
for( let v=2;v<=n;v++ ){ //枚举合并的堆数的规模
for( let i=1;i<=n-v+1;i++ ){ //枚举起始点i
let j = i+ v-1; //枚举j点
min[i][j] = Infinity; //初始化最大值
max[i][j] = -1; //初始化为-1
let w = sum[j] - sum[i-1]; //记录i...j之间的石子数之和
// 枚举中间分割点:k>=i,k<j
for( let k=i;k<j;k++ ){
min[i][j] = Math.min( min[i][j], min[i][k] + min[k+1][j] + w );
max[i][j] = Math.max( max[i][j], max[i][k] + max[k+1][j] + w );
}
}
}
}
function circular( a,n ){
for( let i =1;i<=n-1;i++ ){ //2*n-1
a[n+i] = a[i];
}
n = 2*n-1;
straight(a,n);
n=(n+1)/2;
min_circular = min[1][n];
max_circular = max[1][n];
for( let i=2;i<=n;i++ ){
if( min[i][n+i-1] < min_circular ){
min_circular = min[i][n+i-1];
}
if( max[i][n+i-1] > max_circular ){
max_circular = max[i][n+i-1];
}
}
}
156、最大价值----可拆分-----贪心算法
毛驴可载重为一个固定值m,洞中有宝物n件,宝物的重量和价值分别为w v,宝物可拆分,如何挑选可得到最大价值的宝物(按照性价比由大到小挑选)
// 求最大宝物价值,可拆分,选择性价比的原则
let n = 6, //宝物数量
w = 19; //毛驴载重量
// 6行,每行包括宝物的重量和价值,后面经过处理在吗 每行的末尾加上性价比
let arr = [];
for( let i=0;i<n;i++ ){
let tmp = []; //每次循环都重新声明,所以不会共享内存
tmp = readline().split(' ');
tmp.forEach( (val,key)=>tmp[key]= ~~val ); //~~转为整数
tmp.push( parseFloat( tmp[1]/tmp[0] ) ); //性价比
arr.push(tmp);
}
arr.sort( (a,b) => b[2]-a[2] ); //按照性价比又高到低排序
let sum = 0; //存放价值量
for( let i=0;i<n;i++ ){//对排序好的进行累加
if( arr[i][0] < w ){ //宝物重量小于毛驴的剩下载重量,则进行处理
w -= arr[i][0]; //毛驴剩下载重量
sum += arr[i][1] //累加可以载的宝物的价值
}else{
// 宝物重量>毛驴剩下载量
sum += w * arr[i][2];
break;
}
}
console.log(sum);
156、最大价值----不可拆分-----动态规划 0-1背包问题
分配购物车容量m,有n个物品,每个物品重量、价值分别为w v,物品不可拆分,在不超重的情况下如何挑选物品使总价值最大,挑选的物品具体是什么,最大价值?
// 不是最优方法
let n = 5, //物品个数
m = 10; //购物车容量
// let w =readline().split(' '); //每个物品的重量
// w = w.forEach( (val,key) => w[key] = ~~val );
let w=[1,2,3,4,5];
w.unshift(0);//使第一件物品对应下标1
// let v =readline().split(' '); //每个物品的价值
// v = v.forEach( (val,key) => v[key] = ~~val );
let v = [6,3,5,4,6];
v.unshift(0);//使第一件物品价值对应下标1
let c = [];//第i件物品放入载量j的总价值c[i][j]
let tmp = [];
for( let i=0;i<=m;i++ ){
tmp.push(0);
}
c.push(tmp);
// 初始化第0行,0列为0
for( let i=1;i<=n;i++ ){
tmp = [];
tmp[0] = 0;
c.push(tmp);
}
for( let i=1;i<=n;i++ ){ //计算c[i][j]
for( let j=1;j<=m;j++ ){
if( w[i] > j ){ //当物品重量大于购物车容量不放入
c[i][j] = c[i-1][j];
}else{
// 否则比较此物品放和不放是否使得购物车内总价值最大
c[i][j] = Math.max( c[i-1][j], c[i-1][j-w[i]] + v[i] );
}
}
}
console.log('maxval: '+ c[n][m]);
// 逆向构造最优解
let h = w; //购物车容量
let x =[0]; //1表示放进去,0-不放
for( let i=n;i>0;i-- ){
if( c[i][h] > c[i-1][h] ){//说明i物品放进容量j
x.push(1);
h -= w[i]; //购物车剩余容量
}else{
x.push(0);
}
}
console.log('装入购物车的物品为:');
x.forEach( (val,key)=>{
if( val ===1 ){
console.log(key);
}
});
优化算法:
157、加工顺序,时间最短
n个零件,每个零件先由机器1处理后由机器2处理,时间t1i t2i,
贝尔曼规则:
(1)第一台机器上加工时间越短越先加工
(2)第二台机器上加工时间越长越先加工
(3)第一个机器上加工时间小于第二个加工时间先加工
(4)第一个机器上加工时间小大于等于第二个加工时间后加工
算法:
(1)将零件分成两个集合N1={ i| t1i < t2i }第一个机器上加工时间小于第二个机器上加工时间;N2={ i| t1i >= t2i }第一个机器上加工时间大于等于第二个机器上加工时间;
(2)N1按照非递减的顺序排序(t1i时间小的排在前面)(排序后:1 2 2 3 4 4) ;N2按照非递增排序(t2i时间大的排在前面)(排序后:6 6 5 4 3)
(3)拼接N1N2就是加工顺序
//
let arr = [];
let n = 7; //加工零件个数
// let tmp = [];
let tmp = [[3,7],[8,2],[10,6],[12,18],[6,3],[9,10],[15,4]];
for( let i=0;i<n;i++ ){
// let tmp = [];
let obj = {}; //每次都会重新定义,所以不会共享内存
// tmp = readline().split(' ');
obj.id = i;
// obj.x = ~~tmp[0];
obj.x = ~~tmp[i][0]; //这样定义,也是这样obj.x才能访问到
obj.y = ~~tmp[i][1];
arr.push(obj);
}
// 分集合N1(t1i < t2i) N2(t1i >= t2i)
let N1 = [],
N2 = [],
item;
for( let i=0;i<n;i++ ){
item = arr[i];
if( item.x < item.y ){
N1.push(item);
}else{
N2.push(item);
}
}
// N1非递减,N2非递增
N1.sort( (a,b) => a.x - b.x );
N2.sort( (a,b) => b.y - a.y );
arr = N1.concat(N2); //结果
console.log("最优加工顺序:");
arr.forEach( val=>console.log(val.id) );
// 加工时间
let f1 =0,
f2 = 0;
for( let i=0;i<n;i++ ){
f1 += arr[i].x;
f2 = Math.max(f1,f2) + arr[i].y;
}
console.log(f2);
或略掉小数点后多余的0
console.log( parseFloat(123.00000) ) //123
console.log( parseFloat(123.010000) ) //123.01
console.log( parseFloat(123) )//123
小Q定义了一种数列称为翻转数列:
给定整数n和m, 满足n能被2m整除。对于一串连续递增整数数列1, 2, 3, 4..., 每隔m个符号翻转一次, 最初符号为'-';。
例如n = 8, m = 2, 数列就是: -1, -2, +3, +4, -5, -6, +7, +8.
而n = 4, m = 1, 数列就是: -1, +2, -3, + 4.
小Q现在希望你能帮他算算前n项和为多少
//一个数列共有n/2m组,每一组的和为m^2
//所以,前n项和为:(n/2m)*(m^2)=m*n/2
let arr = readline().split(' ');
let n = ~~arr[0];
let m = ~~arr[1];
let com = ~~(n/2/m);
let sum = (m ** 2) * com;
print(sum);
牛牛和羊羊正在玩一个纸牌游戏。这个游戏一共有n张纸牌, 第i张纸牌上写着数字ai。
牛牛和羊羊轮流抽牌, 牛牛先抽, 每次抽牌他们可以从纸牌堆中任意选择一张抽出, 直到纸牌被抽完。
他们的得分等于他们抽到的纸牌数字总和。
现在假设牛牛和羊羊都采用最优策略, 请你计算出游戏结束后牛牛得分减去羊羊得分等于多少
//最优策略---每次每个人拿的数都是剩下里面最大的数
//对数据按照非升序排序
let n = ~~readline();
let arr = readline().split(' ');
arr.forEach((val,key)=>arr[key]= ~~val);
arr.sort((a,b)=>b-a);
let sum1 = 0, //牛牛
sum2 = 0; //洋洋
for( let i=0;i<n;i++ ){
if( Math.floor((i%2))== 0 ){
sum1 += arr[i];
}else{
sum2 += arr[i];
}
}
print(sum1-sum2);
let n = ~~readline();//天数
let m = ~~readline();//块数
function sum(a){
//a是第一天吃的块数
//这里是求第一天吃a块,n天吃的块数
let count = 0;
for(let i=0;i<n;i++){
count += a;
a = Math.ceil(a/2);
}
return count;
}
let low = 1;
let high = m;
let mid;
let first;
while(low<high){
mid = Math.ceil((low+high)/2);
first = sum(mid);
if( first == m ){
break;
}else if( first < m ){
low += 1;
}else{
hight--;
}
}
print(first);
判断数组B是否是数组A的子集
针对数组是否是有序和无序进行不同的处理
// 无序、有重复
// 无序,那么对B的每一个元素遍历,每次遍历一个元素的时候就在A中找,如果A中没有返回false,如果有则
// 利用indexOf()找出该元素在A数组中的位置,然后用splice()将该元素删除
// function subset(A,B){
// let len1 = A.length,
// len2 = B.length,
// tmp;
// for(let i=0, len=B.length; i<len; i++){
// tmp = A.indexOf(B[i]);
// if( tmp === -1){
// return false;
// }else{
// A.splice( A.indexOf(B[i]), 1);
// }
// }
// return true;
// }
// let A = [3,2,1,4,4];
// let B = [4,4];
// console.log( subset(A,B) );
// 对于有序的话,对B遍历,每次都判断在B中的当前元素在A中是否存在,如果存在就将标志A中的元素的下标加1,
// 下次在A中查找元素的话,就会从该下标开始
function subset(A,B){
let len1 = A.length,
len2 = B.length,
index = 0;
// 判断边界条件
if( len1 < len2 ){
return false;
}else if( B[0] > A[len1-1] || B[len2-1] > A[len1-1] ){
return false;
}
// 对B遍历
for(let i=0; i<len2; i++){
index = A.indexOf(B[i],index);
if( index === -1){
return false;
}else{
index++;
}
}
return true;
}
let A = [3,4,4];
let B = [4,4,8];
console.log( subset(A,B) );
200、在移动端实现特殊链接---利用H5的js提供的接口
1)打电话
//1、常用方式
<a href="tel:10086">10086</a>
//2、使用wtai协议进行拨打电话
<a href="wtai://wp/mc;10086">10086</a>
2)发短信
//格式
sms:<phone-number>[,<phone-number>]*[?body=<message_body>]
//例子
<a href="sms:10086">给 10086 发短信</a><br />
<a href="sms:10086?body=cxye">给 10086 发送内容为"cxye"的短信</a><br />
<a href="sms:10086,10010?body=cxye">给 10086 和 10010 发送内容为"cxye"的短信</a>
3)发邮件,使用mailto
<a href="mailto:test1@163.com">mail</a>
<a href="mailto:test1@163.com,test2@126.com">mail</a>
<a href="mailto:test1@163.com?subject=Testing">mail</a>
<a href="mailto:test1@163.com?subject=Testing mailto&cc=test3@126.com">mail</a>
4)地图定位GPS
//格式
<a href="geopoint:[经度],[纬度]">我的位置</a>
//例子
<a href="geopoint:108.954823,34.275891">我的位置</a>
201、输出结果
1、
function switchCase(value){
switch(value){
case '0':console.log('case 0');
case '1':console.log('case 1');break;
case undefined:console.log('undefined');break;
default:console.log('default');
}
}
// 写出下列输出结果
switchCase(0); //default-----不会发生转换
switchCase('0'); //case 0 case 1-----因为case '0'后面没有break,所以继续执行
switchCase(); //undefined-----没有参数,默认为undefined
2、
用ES6解构的方式,将下面代码中的obj.name赋值给n,obj.age赋值给a
let obj = {name:’韩梅梅’, age:’20’};
let n, a
实现:
let {name: n, age: a} = obj;
3、
var s = {
s: 'student',
getS: function(){
console.log(this.s);
}
};
var t = {
s: 'teaher'
};
var getS = s.getS;
var getS1 = getS.bind(s);
// 写出以下输出结果
s.getS(); //student
s.getS.apply(t); //teaher
getS(); //{s: "student", getS: ƒ},this=window,window.s刚好是s对象
getS1.call(t); //student
202、 用js实现一个随机打乱数组顺序的函数,要求可以设定数组种任意1个元素的位置不变,其他位置的元素位置随机变化
//当产生的随机数和传进来的索引不一样的时候,才会返回随机数
function randNum(low,high,unChangeIndex){
let tmp;
do{
tmp = Math.round( Math.random()*(high-low) + low );
}while(tmp == unChangeIndex);
return tmp;
}
function mixSort(arr,ele){
let len = arr.length,
index = arr.indexOf(ele),
tmpIndex,
tmpEle;
//对数组的每一个元素(除了传进来位置不变的元素外)遍历,实现混排
for(let i=0;i<len;i++){
if( i!== index ){
tmpIndex = randNum( 0,i ,index);
tmpEle = arr[tmpIndex];
arr[tmpIndex] = arr[i];
arr[i] = tmpEle
}
}
}
let arr = [1,2,3,4];
mixSort(arr,3)
console.log(arr);
202、 比较两个版本号的 大小
function compare(str1,str2){
let arr1 = str1.split('.'),
arr2 = str2.split('.'),
len1 = arr1.length,
len2 = arr2.length;
//将两个数组的长度补到一样长
if( len1 < len2 ){
for( let i=0;i<len2-len1;i++ ){
arr1.push('0');
}
len1 = len2;
}else if( len1 > len2 ){
arr2.push('0');
len2 = len1;
}
for( let i=0;i<len1;i++ ){//str1<str2,-1; str1==str2,0; str1>str2,1
if( arr1[i] < arr2[i]){
return -1;
}else if( arr1[i] > arr2[i] ){
return 1;
}else{
continue;
}
}
return 0;
}
let str1 = '1.0.2';
let str2 = '2.0';
console.log( compare(str1,str2) );
203、 用css3画圆,椭圆,三角形
1)三角形原理:通过border来设置,理解盒子模型,就可以设置任意三角形
<div class='div1'></div>
.div1{
width: 100;
height: 100;
border-left: 100px solid red;
border-right: 100px solid black;
border-bottom: 100px solid green;
border-top:100px solid yellow;
}
效果:整个盒子的宽度为width+border-left-width+ border-rigth-width=100+100+100,heigth同理
如果将上面的height:0其他代码不变,显示效果如下,
把heigth=0,width=0,其他不变,显示如下
如果,不设置border-bottom,默认为0,显示效果如下
如果把border-left border-right的color设为transparent,显示:
画直角三角形
.div1{
width: 0;
height: 0;
border-left: 100px solid red;
border-top:100px solid transparent;
}
2)画梯形和画三角形的原理一样,通过设置border在结合border-color为transparent
如果设置的是上(下)梯形,那么width不能为0,如果设置的是左右梯形,那么height不能为0
.div1{
width: 100px;
height: 100px;
border-left: 100px solid transparent;
border-right: 100px solid transparent;
border-bottom: 100px solid transparent;
border-top:100px solid yellow;
}
效果如下
3)圆形原理---利用width=height(先画方形),在设置border-radius:50%
画一个圆
<div class='div1'></div>
.div1{
width: 100px;
height: 100px;
border-radius: 50%;
background-color:red;
}
效果:
画同心圆:div里面内嵌子div,一级套一级,然后利用垂直水平居中技术实现同心圆
<div class='div1'>
<div class='div2'></div>
</div>
.div1{
position:relative;
width: 100px;
height: 100px;
border-radius:50%;
background-color:red;
text-align: center;
}
.div2{
position:absolute;
top:0;
right:0;
bottom:0;
left:0;
margin:auto;
width: 50px;
height: 50px;
border-radius:50%;
background-color:green;
}
<div class='div1'>
<div class='div2'>
<div class="div3"></div>
</div>
</div>
.div1{
position:relative;
width: 100px;
height: 100px;
border-radius:50%;
background-color:red;
}
.div2{
position:absolute;
top:0;
right:0;
bottom:0;
left:0;
margin:auto;
width: 50px;
height: 50px;
border-radius:50%;
background-color:green;
}
.div3{
position:absolute;
top:0;
right:0;
bottom:0;
left:0;
margin:auto;
width: 30px;
height: 30px;
border-radius:50%;
background-color:white;
}
4)画椭圆,先画矩形,在设置border-radius:50%
.div1{
position:relative;
width: 200px;
height: 100px;
border-radius:50%;
background-color:red;
}
203、 js中的按位非(~)的本质:操作数的负值减1----和c有区别
//~按位非:操作数的负值减1
console.log( ~(2+'3') ); //-24
console.log( ~undefined ); //-1
console.log(~NaN); //-1
输出结果
let y = 0.3-0.2;
let z = 7;
let obj = {a:10};
let change = function(obj){
if(obj instanceof Object){
if(y===0.1){
obj.a = 8;
}else{
obj = { t:'mumble' };
}
}else{
obj=9;
}
}
change(obj);
change(z);
console.log('y '+y); //0.09999999999999998
console.log('z '+z); // 7
console.log('obj '+obj); // { t:'mumble' }
204、 js中的eval(str)函数和with()函数
严格模式下不允许使用with语句
with 语句的作用是将代码的作用域设置到一个特定的对象中;目的是暂时改变作用域,简化多次编写同一个对象的工作
调用with(obj)的时候,函数会创建一个新的活动对象,将该活动对象推到作用域的最前端,该对象就是with的对象。这就意味着所有的局部变量都处于第二个作用域链对象中去了,这也就是为什么要避免使用with的原因
var a = 123;
var b = {a : 321};
with(b){
console.log(a); // 321
}
var a = 123;
var b = {}; 这里去掉b中的a属性
with(b){
console.log(a); // 123
} //从作用域链来分析
eval(str)
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码
var obj = eval("({age:26, name:'rose'})");//返回一个对象
console.log(obj.age);
204、 伪元素(::)和伪类(:)
https://www.cnblogs.com/Yfling/p/7259949.html 详细介绍
伪类的效果可以通过添加一个实际的类来达到,而伪元素的效果则需要通过添加一个实际的元素才能达到
CSS3为了区分伪类和伪元素,已经明确规定了伪类用一个冒号来表示,而伪元素则用两个冒号来表示。但因为兼容性的问题,所以现在大部分还是统一的单冒号,但是抛开兼容性的问题,我们在书写时应该尽可能养成好习惯,区分两者
单冒号(:)用于css3伪类,双冒号(::)用于CSS3伪元素。伪元素由双冒号和伪元素名称组成。不过浏览器需要同时支持旧的已经存在的伪元素写法,比如:first-line、:first-letter、:before、:after等,而新的在CSS3中引入的伪元素则不允许再支持旧的单冒号的写法
伪元素:用于将特殊的效果添加到某些选择器。伪元素代表了某个元素的子元素,这个子元素虽然在逻辑上存在,但却并不实际存在于文档树中
伪类:
伪元素:
使用css3的animation实现摆钟的效果
<div class="clock-box">
<div class="clock"></div>
</div>
.clock-box {
width:300px;
height:300px;
margin:100px auto;
border:1px solid #00ff90;
}
.clock {
width: 2px;
height: 100px;
background: #000000;
margin: 0 auto;
position: relative;
-webkit-animation: go 1s ease-in-out alternate infinite;
/*animation-direction:alternate 规定是否应该轮流反向播放动画*/
-moz-animation: go 1s ease-in-out alternate infinite;
animation: go 1s ease-in-out alternate infinite;
}
.clock::after { //用伪元素设置小球
content: "";
position: absolute;
bottom: -10px; //这个值是为了让小球和竖线看起来在一起
left: -10px; //设置为伪元素的width/2,让其看起来竖线在小球的正中间
width: 20px;
height: 20px;
border-radius: 10px;
background: #ff0000;
}
@keyframes go {
0% {
-webkit-transform: rotate(30deg);
-moz-transform: rotate(30deg);
transform: rotate(30deg); //旋转的角度
transform-origin: center top; //旋转的中心
-webkit-transform-origin: center top;
-moz-transform-origin: center top;
}
100% {
-webkit-transform: rotate(-30deg);
-webkit-transform-origin: center top;
-moz-transform: rotate(-30deg);
-moz-transform-origin: center top;
transform: rotate(-30deg);
transform-origin: center top;
}
}
介绍下css3的animation属性
属性animation配合@keyframes一起实现动画
animation:所有动画属性的简写属性,除了 animation-play-state 属性
animation-name:规定 @keyframes 动画的名称,自己定
animation-duration:规定动画完成一个周期所花费的秒或毫秒。默认是 0,2s
animation-timing-function:规定动画的速度曲线。默认是 "ease"
animation-delay:规定动画何时开始,默认为0
animation-iteration-count:规定动画被播放的次数。默认是 1
animation-direction:规定动画是否在下一周期逆向地播放。默认是 "normal"
animation-play-state:规定动画是否正在运行或暂停。默认是 "running"
animation-fill-mode:规定对象动画时间之外的状态
css3的transform
transform可以有的属性值
translate(x,y)----水平方向平移
rotate(xdeg)----旋转的度数
scale(num)----缩放
skew(xdeg)------倾斜
transform-origin设置旋转元素的基点,默认是本身的中心为旋转的基点transform-origin:center center
transform-origin: x-axis y-axis z-axis;
/*左上角*/
transform-origin:left top
/*上中间*/
transform-origin:center top
/*右上角*/
transform-origin:right top
/*右中间*/
transform-origin:right center
/*右下角*/
transform-origin:right bottom
/*下中间*/
transform-origin:center bottom
/*左下角*/
transform-origin:left bottom
/*左中间*/
transform-origin:left center
css3的transition属性介绍
transition 属性是一个简写属性,用于设置四个过渡属性
transition: property duration timing-function delay;
transition和animation的区别
相同点:
(1)指定要监听的CSS属性的变化
(2)设置定时函数改变的一个属性值变换到另一个属性值的速率
(3)指定一个时间来控制动画或过渡会花多长时间
(4)程序式描述动画和过渡的事件
(5)CSS属性变化可视化
不同点:
主要体现在触发方式,循环方式,定义关键帧的个数,修改多个css属性的方式,与JS的交互
(1)触发条件不同,transition需要显示触发改变的css的值,animation不需要显示触发。默认自动播放
transition通常和hover等事件配合使用,由事件触发;
一个常见的场景是你使用:hover伪类来改变CSS属性的值;
触发一个过渡的另一种方法是使用JavaScript以编程方式,添加或删除CSS类来模拟一个CSS属性改变
animation动画则不需要任何显式的触发。一旦你定义动画,它将自动开始播放。
(2)循环次数的控制
animation设置循环的次数,通过设置animation-iteration-count:num | infinite,设定固定的循环次数,或者无限循环
transition过渡没有一个属性可以指定多少次运行。当过渡触发时,只运行一次。不过可以通过transitionEnd事件来设置,相对于动画比较复杂
(3) 定义关键帧的个数不一样
animation可以定义多个帧,从而方便控制CSS的属性值,不仅仅是开始和结束
transition只能控制start和end两个关键帧的css属性值
(4) 修改多个属性的方式不一样
/*transition:如果变化涉及到一个以上属性,则需要一次性定义需要改变的css属性*/
#mainContent {
background-color: #CC0000;
transition:background-color .5s ease-in, width .5s ease-in
}
#mainContent:hover {
cursor: pointer;
background-color: #000000;
width: 500px;
}
/* animation:则通过帧可以添加任何属性变化 */
@keyframes imageSlide {
0% {
left: -150px;
}
20% {
left: 50px;
height: 200px;
}
80% {
left: 200px;
height:300px;
}
100% {
left: 600px;
background-color:#FFFFFF;
}
}
(5)与JS的相互作用
transition经常跟JS搭配,让js改动属性,从而触发transition的操作,而animation与js的交互不紧密
结论:
1. 如果要灵活定制多个帧以及循环,用animation;如果仅仅是简单的from和to 效果,用 transition
2. 如果要使用js灵活设定动画属性,用transition
CSS3 有3种和动画相关的属性:transform, transition, animation
transform 描述了元素静态样式,而transition 和 animation 能实现动画效果,所以transform 常常配合后两者使用
用原生js动态创建li,然后挂载到dom中,然后点击相应的li输出对应的索引
挂载到dom中有两种方法:设置ul的innerHTML,或者利用createElement()和appendChild()来设置
<style>
ul{
display:block;
list-style: none;
}
ul li{
background:red;
height:50px;
font-size:16px;
}
</style>
<ul class='ul1'></ul>
<script>
let ul = document.getElementsByClassName('ul1')[0];
// 两种方式实现挂载,设置innerHTML,或者通过创建li,然后通过appendChild()来设置
// for(let i=0;i<10;i++){
// let li = document.createElement('li');
// li.innerHTML = 'fdsfsf';
// ul.appendChild(li);
// }
let str = '';
for(let i=0;i<10;i++){
str += '<li>fhdfhu</li>';
}
ul.innerHTML = str;
// 挂载之后才能获取到li节点
let li = ul.children;
for(let i=1;i<=10;i++){
li[i-1].onclick = (function(a){
return function(){console.log(a);}
})(i);
}
</script>
实现小球的滚动
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<script src="jquery-3.1.1.min.js"></script>
<style>
body{
background: black;
}
.space{
width: 400px;
height: 200px;
position: relative;
border-bottom: 3px solid white;
}
.wheel{
width: 50px;
height: 50px;
border-radius: 50%;
border: 2px solid white;
position: absolute;
left: 0px;
bottom: 0px;
animation: move 3s linear infinite alternate;
}
/*用伪元素实现圆形正中间的竖线*/
.wheel::before{
content: "";
display: block;
position: absolute;
/*这些值是根据容器的width height来设置的*/
width: 25px; /*是.wheel的width/2*/
height: 2px; /*任意设置*/
left: 25px; /*根据.wheel的width来设置*/
top: 25px; /*根据.wheel的height来设置*/
background-color: white;
}
@keyframes move{
0%{
/*初始的时候不需要水平移动和旋转角度*/
transform: translateX(0px) rotate(0deg);
}
100%{
/*水平移动的距离=容器width(滚动距离) - 小球的直径*/
/*旋转角度 = 水平移动的距离/(小球周长:2*pi*r) *360° */
transform: translateX(350px) rotate(802deg);
}
}
</style>
</head>
<body>
<div class='space'>
<div class='wheel'>
</div>
</div>
<script>
</script>
</body>
</html>
这里的轮子是围绕自己的圆心转的,transform-origin:center center
用css3实现地球绕太阳转
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<script src="jquery-3.1.1.min.js"></script>
<style>
body{
background: #000;
}
/*sun也在动,只不过其是绕自身的中心在转动*/
.sun{
margin-top: 200px;
margin-left: 300px;
width: 280px;
height: 280px;
border-radius: 50%;
border:1px solid #FFA500;
background:#FFA500;
box-shadow:0px 0px 35px #FFA500;
animation:action 10s infinite linear;
}
@keyframes action{
0% { transform: rotate(0deg) }
100% { transform: rotate(360deg)}
}
.earth{
/*让earth偏移sun*/
margin-left: -90px;
width: 40px;
height: 40px;
border-radius: 50%;
border:1px solid #0000CC;
background:#0000CC;
box-shadow:0px 0px 35px #0000CC;
animation:actions 5s infinite linear;
}
@keyframes actions{
0% {
transform: rotate(0deg) ;
transform-origin:center center;
}
100% { transform: rotate(360deg)}
}
.moon{
margin-left: -20px;
width: 10px;
height: 10px;
border-radius: 50%;
border:1px solid #F0F0F0;
box-shadow:0px 0px 35px #F0F0F0;
background:#F0F0F0;
}
</style>
</head>
<body>
<div class="sun">
<div class="earth">
<div class="moon"></div>
</div>
</div>
</body>
</html>
199、实现数字千分位和大写
// 实现千分位转化,只是针对正整数有效,如果是有符号或者小数的话,需要进行处理
let strNum = '1234556';
//正则正向引用
let result1 = strNum.replace( /\d{1,3}(?=(\d{3})+$)/g,function(s){
return s+',';
} );
console.log(result1);
let result2 = '';
result2 = AmountInWords('1400398');
let result3 = result1 + ' ' + result2;
console.log(result3);
function AmountInWords(dValue, maxDec)
{
// 验证输入金额数值或数值字符串:
dValue = dValue.toString().replace(/,/g, ""); dValue = dValue.replace(/^0+/, ""); // 金额数值转字符、移除逗号、移除前导零
if (dValue == "") { return "零元整"; } // (错误:金额为空!)
else if (isNaN(dValue)) { return "错误:金额不是合法的数值!"; }
var minus = ""; // 负数的符号“-”的大写:“负”字。可自定义字符,如“(负)”。
var CN_SYMBOL = ""; // 币种名称(如“人民币”,默认空)
if (dValue.length > 1)
{
if (dValue.indexOf('-') == 0) { dValue = dValue.replace("-", ""); minus = "负"; } // 处理负数符号“-”
if (dValue.indexOf('+') == 0) { dValue = dValue.replace("+", ""); } // 处理前导正数符号“+”(无实际意义)
}
// 变量定义:
var vInt = ""; var vDec = ""; // 字符串:金额的整数部分、小数部分
var resAIW; // 字符串:要输出的结果
var parts; // 数组(整数部分.小数部分),length=1时则仅为整数。
var digits, radices, bigRadices, decimals; // 数组:数字(0~9——零~玖);基(十进制记数系统中每个数字位的基是10——拾,佰,仟);大基(万,亿,兆,京,垓,杼,穰,沟,涧,正);辅币(元以下,角/分/厘/毫/丝)。
var zeroCount; // 零计数
var i, p, d; // 循环因子;前一位数字;当前位数字。
var quotient, modulus; // 整数部分计算用:商数、模数。
// 金额数值转换为字符,分割整数部分和小数部分:整数、小数分开来搞(小数部分有可能四舍五入后对整数部分有进位)。
var NoneDecLen = (typeof(maxDec) == "undefined" || maxDec == null || Number(maxDec) < 0 || Number(maxDec) > 5); // 是否未指定有效小数位(true/false)
parts = dValue.split('.'); // 数组赋值:(整数部分.小数部分),Array的length=1则仅为整数。
if (parts.length > 1)
{
vInt = parts[0]; vDec = parts[1]; // 变量赋值:金额的整数部分、小数部分
if(NoneDecLen) { maxDec = vDec.length > 5 ? 5 : vDec.length; } // 未指定有效小数位参数值时,自动取实际小数位长但不超5。
var rDec = Number("0." + vDec);
rDec *= Math.pow(10, maxDec); rDec = Math.round(Math.abs(rDec)); rDec /= Math.pow(10, maxDec); // 小数四舍五入
var aIntDec = rDec.toString().split('.');
if(Number(aIntDec[0]) == 1) { vInt = (Number(vInt) + 1).toString(); } // 小数部分四舍五入后有可能向整数部分的个位进位(值1)
if(aIntDec.length > 1) { vDec = aIntDec[1]; } else { vDec = ""; }
}
else { vInt = dValue; vDec = ""; if(NoneDecLen) { maxDec = 0; } }
if(vInt.length > 44) { return "错误:金额值太大了!整数位长【" + vInt.length.toString() + "】超过了上限——44位/千正/10^43(注:1正=1万涧=1亿亿亿亿亿,10^40)!"; }
// 准备各字符数组 Prepare the characters corresponding to the digits:
digits = new Array("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"); // 零~玖
radices = new Array("", "拾", "佰", "仟"); // 拾,佰,仟
bigRadices = new Array("", "万", "亿", "兆", "京", "垓", "杼", "穰" ,"沟", "涧", "正"); // 万,亿,兆,京,垓,杼,穰,沟,涧,正
decimals = new Array("角", "分", "厘", "毫", "丝"); // 角/分/厘/毫/丝
resAIW = ""; // 开始处理
// 处理整数部分(如果有)
if (Number(vInt) > 0)
{
zeroCount = 0;
for (i = 0; i < vInt.length; i++)
{
p = vInt.length - i - 1; d = vInt.substr(i, 1); quotient = p / 4; modulus = p % 4;
if (d == "0") { zeroCount++; }
else
{
if (zeroCount > 0) { resAIW += digits[0]; }
zeroCount = 0; resAIW += digits[Number(d)] + radices[modulus];
}
if (modulus == 0 && zeroCount < 4) { resAIW += bigRadices[quotient]; }
}
resAIW += "元";
}
// 处理小数部分(如果有)
for (i = 0; i < vDec.length; i++) { d = vDec.substr(i, 1); if (d != "0") { resAIW += digits[Number(d)] + decimals[i]; } }
// 处理结果
if (resAIW == "") { resAIW = "零" + "元"; } // 零元
if (vDec == "") { resAIW += "整"; } // ...元整
resAIW = CN_SYMBOL + minus + resAIW; // 人民币/负......元角分/整
return resAIW;
}
实现数字大写:只是简单转换,没有考虑边界因素
function turnLowerToUpper(num){
let str = num.toString(),
zeroCount = 0,
digits = new Array("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"),
radices = new Array("", "拾", "佰", "仟"),
bigRadices = new Array("", "万", "亿", "兆", "京", "垓", "杼", "穰" ,"沟", "涧", "正"),
ret = '',
len = str.length,
p,
val,
rem;
for( let i=0;i<len;i++ ){
char = str.charAt(i);
p = len-1-i;
val = parseInt(p/4);
rem = parseInt(p%4);
if( char == '0' ){
zeroCount++;
}else{
if( zeroCount > 0){
ret += digits[0];
}
zeroCount = 0;
ret += digits[parseInt(char)] + radices[rem];
}
if( rem==0 && zeroCount<4 ){
ret += bigRadices[val];
}
}
return ret;
}
console.log(turnLowerToUpper(12345678))
200 、js中实现私有变量的方法
在ES6之前,作用域只包括全局作用域和函数作用域,实现私有变量是通过在函数内定义变量,然后通过闭包去访问该私有变量;
在ES6中是通过在constructor里面定义变量,然后通过set和get来设置和获取私有变量的值
var Book = function(newIsbn, newTitle, newAuthor){
//private attributes
var isbn, title, author;
this.name = 'jiang';
//privileged methods
this.getTitle = function(){
//返回变量title,而不是this.title
return title;
}
this.setTitle = function(newTitle){
title = newTitle || 'No title specified'
}
//Constructor code
this.setTitle(newTitle);
}
//Public, non-privileged methods
Book.prototype = {
display: function(){
console.log(this.getTitle());
}
}
var book = new Book('a', 'book1', 'sysuzhyupeng');
console.log(book.getTitle()); // 'book1'
console.log(book.title) // undefined,只能通过上面的方法访问
console.log(book.name); //jiang,在函数中通过this声明的属性
在es5中就已经可以通过definedProperty方法中的访问器,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
var book = {
//不能是title,必须与title不同,这里使用常用命名规范,在前面加了下划线
_title: 'book1'
}
Object.defineProperty(book, 'title', {
get: function(){
//同样不能是title,使用_title
return this._title;
},
set: function(value){
value == 'book2' ?
this._title = value : ''
}
})
book.title // 'book1'
book.title = 'book3';
book.title // 'book1'
book.title = 'book2';
book.title // 'book2'
es6的写法,也是通过中间变量来访问私有属性的
class Book {
constructor(name){
this._title = name;
}
get title(){
return this._title;
}
set title(value){
this._title = value;
}
}
var book = new Book('book1');
console.log(book.title);// 'book1'
book.title = 'book3';
console.log(book.title);// 'book3'
book.title = 'book2';
console.log(book.title ) // 'book2'
200 、匹配普通邮箱的正则表达式
let reg = /^\w+[-.\w]*@(\w+(-\w+)?\.)+[a-z]{2,6}/
201 、实现单位的转换,bps Kbps Mbps Gbps Tbps,如 1000 bps 转换结果为1 Kbps,四舍五入,保留2位小数,如果两位小数位0,则取整
let str = '1000';
let num = parseInt(str,10);
let len = str.length;
let tmp;
let ret = '';
let unit = ''
if( len<4 ){
tmp = str;
unit = 'bps';
}else if( len>=4 && len <=6 ){
unit = 'Kbps';
tmp = ( num/1000 ).toFixed(2)
}else if( len >=7 && len <= 9 ){
unit = 'Mbps';
tmp = ( num/1000000 ).toFixed(2)
}else if( len>=10 && len <=12 ){
unit = 'Gbps';
tmp = ( num/1000000000 ).toFixed(2)
}else{
unit = 'Tbps';
tmp = ( num/1000000000000 ).toFixed(2)
}
// 保留两位小数,如果小数位1.00则变成1
if( (tmp - parseInt(tmp,10)) < Number.EPSILON ){
tmp = parseInt(tmp);
}
// 加上单位
ret = tmp +" " + unit;
console.log(ret)